示例#1
0
    def __init__(self, iut, etos):
        """Initialize.

        :param iut: IUT to execute tests on.
        :type iut: :obj:`etr.lib.iut.Iut`
        :param etos: ETOS library
        :type etos: :obj:`etos_lib.etos.ETOS`
        """
        self.etos = etos
        self.iut = iut
        self.config = self.etos.config.get("test_config")

        self.log_area = LogArea(self.etos)
        self.iut_monitoring = IutMonitoring(self.iut)
        self.issuer = {"name": "ETOS Test Runner"}
示例#2
0
    def test_start_monitoring_single_script(self):
        """Verify that the start monitoring method can execute a single script.

        Approval criteria:
            - Start monitoring shall execute a script to completion.

        Test steps::
            1. Initialize IUT monitoring.
            2. Load a script to the config.
            3. Start monitoring.
            4. Verify that the script executed.
        """
        self.logger.info("STEP: Initialize IUT monitoring.")
        iut_monitoring = IutMonitoring(None)
        iut_monitoring.interrupt_timeout = 5
        iut_monitoring.terminate_timeout = 5
        iut_monitoring.kill_timeout = 5

        self.logger.info("STEP: Load script to the config.")
        self.config.set("scripts", [{
            "name": str(self.script),
            "parameters": ["world"]
        }])

        self.logger.info("STEP: Start monitoring.")
        iut_monitoring.start_monitoring()

        self.logger.info("STEP: Verify that the script executed.")
        self._wait_for_file(Path.cwd().joinpath("output"))
        with open(Path.cwd().joinpath("output"), encoding="utf-8") as output:
            hello_world = output.read().strip()
            self.logger.info(hello_world)
        self.assertEqual(hello_world, "Hello world")
示例#3
0
    def test_start_monitoring_multiple_scripts(self):
        """Verify that the start monitoring method can execute multiple scripts.

        Approval criteria:
            - Start monitoring shall execute multiple scripts to completion.

        Test steps::
            1. Initialize IUT monitoring.
            2. Load the scripts to the config.
            3. Start monitoring.
            4. Verify that the scripts executed.
        """
        self.logger.info("STEP: Initialize IUT monitoring.")
        iut_monitoring = IutMonitoring(None)
        iut_monitoring.interrupt_timeout = 5
        iut_monitoring.terminate_timeout = 5
        iut_monitoring.kill_timeout = 5

        self.logger.info("STEP: Load the scripts to the config.")
        script = ["#!/bin/bash", "echo Goodbye $1 > $(pwd)/output2"]
        second_script = Path.cwd().joinpath("second.sh")
        self.files.append(second_script)
        self.files.append(Path.cwd().joinpath("output2"))
        with open(second_script, "w", encoding="utf-8") as scriptfile:
            for line in script:
                scriptfile.write(f"{line}\n")
        self.config.set(
            "scripts",
            [
                {
                    "name": str(self.script),
                    "parameters": ["world"]
                },
                {
                    "name": str(second_script),
                    "parameters": ["world"]
                },
            ],
        )

        self.logger.info("STEP: Start monitoring.")
        iut_monitoring.start_monitoring()

        self.logger.info("STEP: Verify that the scripts executed.")
        self._wait_for_file(Path.cwd().joinpath("output"))
        with open(Path.cwd().joinpath("output"), encoding="utf-8") as output:
            hello_world = output.read().strip()
            self.logger.info(hello_world)
        self._wait_for_file(Path.cwd().joinpath("output2"))
        with open(Path.cwd().joinpath("output2"), encoding="utf-8") as output:
            goodbye_world = output.read().strip()
            self.logger.info(goodbye_world)
        self.assertEqual(hello_world, "Hello world")
        self.assertEqual(goodbye_world, "Goodbye world")
示例#4
0
    def __init__(self, iut, etos):
        """Initialize.

        :param iut: IUT to execute tests on.
        :type iut: :obj:`etr.lib.iut.Iut`
        :param etos: ETOS library
        :type etos: :obj:`etos_lib.etos.ETOS`
        """
        self.etos = etos
        self.iut = iut
        self.config = self.etos.config.get("test_config")

        self.iut_monitoring = IutMonitoring(self.iut)
        self.issuer = {"name": "ETOS Test Runner"}

        self.env = os.environ
        if not os.path.isdir(os.getenv("TEST_ARTIFACT_PATH")):
            os.makedirs(os.getenv("TEST_ARTIFACT_PATH"))
        if not os.path.isdir(os.getenv("TEST_LOCAL_PATH")):
            os.makedirs(os.getenv("TEST_LOCAL_PATH"))
        if not os.path.isdir(os.getenv("GLOBAL_ARTIFACT_PATH")):
            os.makedirs(os.getenv("GLOBAL_ARTIFACT_PATH"))
示例#5
0
class TestRunner:
    """Test runner for ETOS."""

    logger = logging.getLogger("ETR")

    def __init__(self, iut, etos):
        """Initialize.

        :param iut: IUT to execute tests on.
        :type iut: :obj:`etr.lib.iut.Iut`
        :param etos: ETOS library
        :type etos: :obj:`etos_lib.etos.ETOS`
        """
        self.etos = etos
        self.iut = iut
        self.config = self.etos.config.get("test_config")

        self.log_area = LogArea(self.etos)
        self.iut_monitoring = IutMonitoring(self.iut)
        self.issuer = {"name": "ETOS Test Runner"}

    def test_suite_started(self):
        """Publish a test suite started event.

        :return: Reference to test suite started.
        :rtype: :obj:`eiffel.events.base_event.BaseEvent`
        """
        suite_name = self.config.get("name")
        categories = ["Regression test_suite", "Sub suite"]
        categories.append(self.iut.identity.name)
        livelogs = self.config.get("log_area", {}).get("livelogs")

        return self.etos.events.send_test_suite_started(
            suite_name,
            links={"CONTEXT": self.etos.config.get("context")},
            categories=categories,
            types=["FUNCTIONAL"],
            liveLogs=[{"name": "console", "uri": livelogs}],
        )

    def main_suite_id(self, activity_id):
        """Get the eiffel event id of the mainsuite linked form the activity triggered event.

        :param activity_id: Id of the activity linked to the main suite
        :type activity_id: str
        :return: Id of the main suite linked from the activity triggered event
        :rtype: str
        """
        for test_suite_started in request_test_suite_started(self.etos, activity_id):
            if not "Sub suite" in test_suite_started["data"]["testSuiteCategories"]:
                return test_suite_started["meta"]["id"]
        raise Exception("Missing main suite events, exiting!")

    def environment(self, context):
        """Send out which environment we're executing within.

        :param context: Context where this environment is used.
        :type context: str
        """
        # TODO: Get this from prepare
        if os.getenv("HOSTNAME") is not None:
            self.etos.events.send_environment_defined(
                "ETR Hostname",
                links={"CONTEXT": context},
                host={"name": os.getenv("HOSTNAME"), "user": "******"},
            )
        if os.getenv("EXECUTION_SPACE_URL") is not None:
            self.etos.events.send_environment_defined(
                "Execution Space URL",
                links={"CONTEXT": context},
                host={"name": os.getenv("EXECUTION_SPACE_URL"), "user": "******"},
            )

    def run_tests(self, workspace):
        """Execute test recipes within a test executor.

        :param workspace: Which workspace to execute test suite within.
        :type workspace: :obj:`etr.lib.workspace.Workspace`
        :return: Result of test execution.
        :rtype: bool
        """
        recipes = self.config.get("recipes")
        result = True
        for num, test in enumerate(recipes):
            self.logger.info("Executing test %s/%s", num + 1, len(recipes))
            with Executor(test, self.iut, self.etos) as executor:
                self.logger.info("Starting test '%s'", executor.test_name)
                executor.execute(workspace)

                if not executor.result:
                    result = executor.result
                self.logger.info("Test finished. Result: %s.", executor.result)
        return result

    def outcome(self, result, executed, description):
        """Get outcome from test execution.

        :param result: Result of execution.
        :type result: bool
        :param executed: Whether or not tests have successfully executed.
        :type executed: bool
        :param description: Optional description.
        :type description: str
        :return: Outcome of test execution.
        :rtype: dict
        """
        if executed:
            conclusion = "SUCCESSFUL"
            verdict = "PASSED" if result else "FAILED"
            self.logger.info(
                "Tests executed successfully. Verdict set to '%s' due to result being '%s'",
                verdict,
                result,
            )
        else:
            conclusion = "FAILED"
            verdict = "INCONCLUSIVE"
            self.logger.info(
                "Tests did not execute successfully. Setting verdict to '%s'",
                verdict,
            )

        suite_name = self.config.get("name")
        if not description and not result:
            self.logger.info("No description but result is a failure. At least some tests failed.")
            description = f"At least some {suite_name} tests failed."
        elif not description and result:
            self.logger.info(
                "No description and result is a success. " "All tests executed successfully."
            )
            description = f"All {suite_name} tests completed successfully."
        else:
            self.logger.info("Description was set. Probably due to an exception.")
        return {
            "verdict": verdict,
            "description": description,
            "conclusion": conclusion,
        }

    def execute(self):  # pylint:disable=too-many-branches,disable=too-many-statements
        """Execute all tests in test suite.

        :return: Result of execution. Linux exit code.
        :rtype: int
        """
        self.logger.info("Send test suite started event.")
        test_suite_started = self.test_suite_started()
        sub_suite_id = test_suite_started.meta.event_id
        main_suite_id = self.main_suite_id(self.etos.config.get("context"))
        self.logger.info("Send test environment events.")
        self.environment(sub_suite_id)
        self.etos.config.set("main_suite_id", main_suite_id)
        self.etos.config.set("sub_suite_id", sub_suite_id)

        result = True
        description = None
        try:
            with Workspace(self.log_area) as workspace:
                self.logger.info("Start IUT monitoring.")
                self.iut_monitoring.start_monitoring()
                self.logger.info("Starting test executor.")
                result = self.run_tests(workspace)
                executed = True
                self.logger.info("Stop IUT monitoring.")
                self.iut_monitoring.stop_monitoring()
        except Exception as exception:  # pylint:disable=broad-except
            result = False
            executed = False
            description = str(exception)
            raise
        finally:
            if self.iut_monitoring.monitoring:
                self.logger.info("Stop IUT monitoring.")
                self.iut_monitoring.stop_monitoring()
            self.logger.info("Figure out test outcome.")
            outcome = self.outcome(result, executed, description)
            pprint(outcome)

            self.logger.info("Send test suite finished event.")
            self.etos.events.send_test_suite_finished(
                test_suite_started,
                links={"CONTEXT": self.etos.config.get("context")},
                outcome=outcome,
                persistentLogs=self.log_area.persistent_logs,
            )

        timeout = time.time() + 600  # 10 minutes
        self.logger.info("Waiting for eiffel publisher to deliver events (600s).")

        previous = 0
        # pylint:disable=protected-access
        current = len(self.etos.publisher._deliveries)
        while current:
            current = len(self.etos.publisher._deliveries)
            self.logger.info("Remaining events to send        : %d", current)
            self.logger.info("Events sent since last iteration: %d", previous - current)
            if time.time() > timeout:
                if current < previous:
                    self.logger.info(
                        "Timeout reached, but events are still being sent. Increase timeout by 10s."
                    )
                    timeout = time.time() + 10
                else:
                    raise Exception("Eiffel publisher did not deliver all eiffel events.")
            previous = current
            time.sleep(1)
        self.logger.info("Tests finished executing.")
        return 0 if result else outcome
示例#6
0
    def test_stop_monitoring_kill(self):
        """Verify that stop monitoring will attempt to kill script that does not terminate.

        Approval criteria:
            - stop monitoring shall send SIGKILL to process if SIGTERM fails.

        Test steps::
            1. Initialize IUT monitoring.
            2. Create and add a script catching SIGINT and SIGTERM.
            3. Start monitoring.
            4. Stop monitoring.
            5. Verify that the script was killed.
        """
        self.logger.info("STEP: Initialize IUT monitoring.")
        iut_monitoring = IutMonitoring(None)
        iut_monitoring.interrupt_timeout = 5
        iut_monitoring.terminate_timeout = 5
        iut_monitoring.kill_timeout = 5

        self.logger.info(
            "STEP: Create and add a script catching SIGINT and SIGTERM.")
        script = [
            "#!/bin/bash",
            "int_handler() {",
            "   echo interrupted! > $(pwd)/output",
            "}",
            "term_handler() {",
            "   echo terminated! >> $(pwd)/output",
            "}",
            "trap int_handler INT",
            "trap term_handler TERM",
            "while :",
            "do",
            "   sleep 1",
            "done",
        ]
        interrupt_script = Path.cwd().joinpath("kill.sh")
        self.files.append(interrupt_script)
        self.files.append(Path.cwd().joinpath("output"))
        with open(interrupt_script, "w", encoding="utf-8") as scriptfile:
            for line in script:
                scriptfile.write(f"{line}\n")
        self.config.set(
            "scripts",
            [
                {
                    "name": str(interrupt_script)
                },
            ],
        )

        self.logger.info("STEP: Start monitoring.")
        iut_monitoring.start_monitoring()
        time.sleep(1)

        self.logger.info("STEP: Stop monitoring.")
        iut_monitoring.stop_monitoring()

        self.logger.info("STEP: Verify that the script was killed.")
        self.assertTrue(self._wait_for_file(Path.cwd().joinpath("output")))
        with open(Path.cwd().joinpath("output"), encoding="utf-8") as output:
            lines = output.readlines()
        interrupt = lines.pop(0).strip()
        self.assertEqual(interrupt, "interrupted!")
        terminate = lines.pop(0).strip()
        self.assertEqual(terminate, "terminated!")
示例#7
0
    def test_stop_monitoring_multiple_scripts(self):
        """Verify that stop monitoring multiple scripts interrupts them.

        Approval criteria:
            - stop monitoring shall send SIGINT to all processes.

        Test steps::
            1. Initialize IUT monitoring.
            2. Create and add multiple scripts with an infinite loop.
            3. Start monitoring.
            4. Stop monitoring.
            5. Verify that the scripts were interrupted with SIGINT.
        """
        self.logger.info("STEP: Initialize IUT monitoring.")
        iut_monitoring = IutMonitoring(None)
        iut_monitoring.interrupt_timeout = 5
        iut_monitoring.terminate_timeout = 5
        iut_monitoring.kill_timeout = 5

        self.logger.info(
            "STEP: Create and add multiple scripts with an infinite loop.")
        script = [
            "#!/bin/bash",
            "int_handler() {",
            "   echo interrupted! > $(pwd)/output",
            "   exit 0",
            "}",
            "trap int_handler INT",
            "while :",
            "do",
            "   sleep 1",
            "done",
        ]
        first_interrupt_script = Path.cwd().joinpath("first_interrupt.sh")
        self.files.append(first_interrupt_script)
        self.files.append(Path.cwd().joinpath("output"))
        with open(first_interrupt_script, "w", encoding="utf-8") as scriptfile:
            for line in script:
                scriptfile.write(f"{line}\n")
        script = [
            "#!/bin/bash",
            "int_handler() {",
            "   echo interrupted! > $(pwd)/output2",
            "   exit 0",
            "}",
            "trap int_handler INT",
            "while :",
            "do",
            "   sleep 1",
            "done",
        ]
        second_interrupt_script = Path.cwd().joinpath("second_interrupt.sh")
        self.files.append(second_interrupt_script)
        self.files.append(Path.cwd().joinpath("output2"))
        with open(second_interrupt_script, "w",
                  encoding="utf-8") as scriptfile:
            for line in script:
                scriptfile.write(f"{line}\n")
        self.config.set(
            "scripts",
            [
                {
                    "name": str(first_interrupt_script)
                },
                {
                    "name": str(second_interrupt_script)
                },
            ],
        )

        self.logger.info("STEP: Start monitoring.")
        iut_monitoring.start_monitoring()
        time.sleep(1)

        self.logger.info("STEP: Stop monitoring.")
        iut_monitoring.stop_monitoring()

        self.logger.info(
            "STEP: Verify that the scripts were interrupted with SIGINT.")
        self.assertTrue(self._wait_for_file(Path.cwd().joinpath("output")))
        self.assertTrue(self._wait_for_file(Path.cwd().joinpath("output2")))
        with open(Path.cwd().joinpath("output"), encoding="utf-8") as output:
            text = output.read().strip()
        self.assertEqual(text, "interrupted!")
        with open(Path.cwd().joinpath("output2"), encoding="utf-8") as output:
            text = output.read().strip()
        self.assertEqual(text, "interrupted!")
示例#8
0
class TestRunner:
    """Test runner for ETOS."""

    logger = logging.getLogger("ETR")

    def __init__(self, iut, etos):
        """Initialize.

        :param iut: IUT to execute tests on.
        :type iut: :obj:`etr.lib.iut.Iut`
        :param etos: ETOS library
        :type etos: :obj:`etos_lib.etos.ETOS`
        """
        self.etos = etos
        self.iut = iut
        self.config = self.etos.config.get("test_config")

        self.iut_monitoring = IutMonitoring(self.iut)
        self.issuer = {"name": "ETOS Test Runner"}

        self.env = os.environ
        if not os.path.isdir(os.getenv("TEST_ARTIFACT_PATH")):
            os.makedirs(os.getenv("TEST_ARTIFACT_PATH"))
        if not os.path.isdir(os.getenv("TEST_LOCAL_PATH")):
            os.makedirs(os.getenv("TEST_LOCAL_PATH"))
        if not os.path.isdir(os.getenv("GLOBAL_ARTIFACT_PATH")):
            os.makedirs(os.getenv("GLOBAL_ARTIFACT_PATH"))

    def test_suite_started(self):
        """Publish a test suite started event.

        :return: Reference to test suite started.
        :rtype: :obj:`eiffel.events.base_event.BaseEvent`
        """
        suite_name = self.config.get("name")
        categories = ["Regression test_suite", "Sub suite"]
        categories.append(self.iut.identity.name)
        livelogs = self.config.get("log_area", {}).get("livelogs")

        return self.etos.events.send_test_suite_started(
            suite_name,
            links={"CONTEXT": self.etos.config.get("context")},
            categories=categories,
            types=["FUNCTIONAL"],
            liveLogs=[{"name": "console", "uri": livelogs}],
        )

    def main_suite_id(self, activity_id):
        """Get the eiffel event id of the mainsuite linked form the activity triggered event.

        :param activity_id: Id of the activity linked to the main suite
        :type activity_id: str
        :return: Id of the main suite linked from the activity triggered event
        :rtype: str
        """
        for test_suite_started in request_test_suite_started(self.etos, activity_id):
            if not "Sub suite" in test_suite_started["data"]["testSuiteCategories"]:
                return test_suite_started["meta"]["id"]
        raise Exception("Missing main suite events, exiting!")

    def confidence_level(self, test_results, test_suite_started):
        """Publish confidence level modified event.

        :param test_results: Results of the test suite.
        :type test_results: bool
        :param test_suite_started: Test suite started event to use as Cause for this confidence.
        :type test_suite_started: :obj:`eiffellib.events.EiffelTestSuiteStartedEvent`
        """
        confidence = self.etos.events.send_confidence_level_modified(
            "{}_OK".format(self.config.get("name")),
            "SUCCESS" if test_results else "FAILURE",
            links={
                "CONTEXT": self.etos.config.get("context"),
                "CAUSE": test_suite_started,
                "SUBJECT": self.config.get("artifact"),
            },
            issuer=self.issuer,
        )
        print(confidence.pretty)

    def environment(self, context):
        """Send out which environment we're executing within.

        :param context: Context where this environment is used.
        :type context: str
        """
        # TODO: Get this from prepare
        if os.getenv("HOSTNAME") is not None:
            self.etos.events.send_environment_defined(
                "ETR Hostname",
                links={"CONTEXT": context},
                host={"name": os.getenv("HOSTNAME"), "user": "******"},
            )

    def run_tests(self, log_handler):
        """Execute test recipes within a test executor.

        :param log_handler: Log handler for gathering logs.
        :type log_handler: :obj:`etr.lib.logs.LogHandler`
        :return: Result of test execution.
        :rtype: bool
        """
        recipes = self.config.get("recipes")
        result = True
        for num, test in enumerate(recipes):
            self.logger.info("Executing test %s/%s", num + 1, len(recipes))
            with Executor(test, self.iut, self.etos) as executor:
                self.logger.info("Starting test '%s'", executor.test_name)
                executor.execute()
                self.logger.info("Gather logs.")
                log_handler.gather_logs_for_executor(executor)

                if not executor.result:
                    result = executor.result
                self.logger.info("Test finished. Result: %s.", executor.result)
        return result

    def outcome(self, result, executed, description):
        """Get outcome from test execution.

        :param result: Result of execution.
        :type result: bool
        :param executed: Whether or not tests have successfully executed.
        :type executed: bool
        :param description: Optional description.
        :type description: str
        :return: Outcome of test execution.
        :rtype: dict
        """
        if executed:
            conclusion = "SUCCESSFUL"
            verdict = "PASSED" if result else "FAILED"
            self.logger.info(
                "Tests executed successfully. "
                "Verdict set to '%s' due to result being '%s'",
                verdict,
                result,
            )
        else:
            conclusion = "FAILED"
            verdict = "INCONCLUSIVE"
            self.logger.info(
                "Tests did not execute successfully. " "Setting verdict to '%s'",
                verdict,
            )

        suite_name = self.config.get("name")
        if not description and not result:
            self.logger.info(
                "No description but result is a failure. " "At least some tests failed."
            )
            description = "At least some {} tests failed.".format(suite_name)
        elif not description and result:
            self.logger.info(
                "No description and result is a success. "
                "All tests executed successfully."
            )
            description = "All {} tests completed successfully.".format(suite_name)
        else:
            self.logger.info("Description was set. Probably due to an exception.")
        return {
            "verdict": verdict,
            "description": description,
            "conclusion": conclusion,
        }

    def execute(self):  # pylint:disable=too-many-branches
        """Execute all tests in test suite.

        :return: Result of execution. Linux exit code.
        :rtype: int
        """
        self.logger.info("Send test suite started event.")
        test_suite_started = self.test_suite_started()
        sub_suite_id = test_suite_started.meta.event_id
        main_suite_id = self.main_suite_id(self.etos.config.get("context"))
        self.logger.info("Send test environment events.")
        self.environment(sub_suite_id)

        self.logger.info("Initialize loghandler.")
        log_handler = LogHandler(
            main_suite_id,
            sub_suite_id,
            self.etos.config.get("context"),
            self.iut,
            self.etos,
        )
        self.logger.info("Start IUT monitoring.")
        self.iut_monitoring.start_monitoring()

        result = True
        description = None
        try:
            self.logger.info("Starting test executor.")
            result = self.run_tests(log_handler)
            executed = True
        except Exception as exception:  # pylint:disable=broad-except
            result = False
            executed = False
            description = str(exception)
            raise
        finally:
            self.logger.info("Stop IUT monitoring.")
            self.iut_monitoring.stop_monitoring()

            self.logger.info("Gather global test logs.")
            log_handler.gather_global_logs()
            self.logger.info("Figure out test outcome.")
            outcome = self.outcome(result, executed, description)
            pprint(outcome)

            self.logger.info("Send test suite finished and confidence events.")
            self.etos.events.send_test_suite_finished(
                test_suite_started,
                links={"CONTEXT": self.etos.config.get("context")},
                outcome=outcome,
                persistentLogs=log_handler.persistent_logs,
            )
            self.confidence_level(result, test_suite_started)
        timeout = time.time() + 30
        self.logger.info("Waiting for eiffel publisher to deliver events (30s).")
        # pylint:disable=len-as-condition, protected-access
        while len(self.etos.publisher._deliveries):
            if time.time() > timeout:
                raise Exception("Eiffel publisher did not deliver all eiffel events.")
            time.sleep(1)
        self.logger.info("Tests finished executing.")
        return 0 if result else outcome