From: "Juraj Linkeš" <juraj.linkes@pantheon.tech>
To: thomas@monjalon.net, Honnappa.Nagarahalli@arm.com,
jspewock@iol.unh.edu, probb@iol.unh.edu, paul.szczepanek@arm.com,
yoan.picchi@foss.arm.com, Luca.Vizzarro@arm.com
Cc: dev@dpdk.org, "Juraj Linkeš" <juraj.linkes@pantheon.tech>
Subject: [RFC PATCH v1 1/5] dts: convert dts.py methods to class
Date: Wed, 20 Dec 2023 11:33:27 +0100 [thread overview]
Message-ID: <20231220103331.60888-2-juraj.linkes@pantheon.tech> (raw)
In-Reply-To: <20231220103331.60888-1-juraj.linkes@pantheon.tech>
The dts.py module deviates from the rest of the code without a clear
reason. Converting it into a class and using better naming will improve
organization and code readability.
Signed-off-by: Juraj Linkeš <juraj.linkes@pantheon.tech>
---
dts/framework/config/__init__.py | 3 -
dts/framework/dts.py | 228 -----------------------------
dts/framework/runner.py | 243 +++++++++++++++++++++++++++++++
dts/main.py | 6 +-
4 files changed, 247 insertions(+), 233 deletions(-)
delete mode 100644 dts/framework/dts.py
create mode 100644 dts/framework/runner.py
diff --git a/dts/framework/config/__init__.py b/dts/framework/config/__init__.py
index 9b32cf0532..497847afb9 100644
--- a/dts/framework/config/__init__.py
+++ b/dts/framework/config/__init__.py
@@ -314,6 +314,3 @@ def load_config() -> Configuration:
config: dict[str, Any] = warlock.model_factory(schema, name="_Config")(config_data)
config_obj: Configuration = Configuration.from_dict(dict(config))
return config_obj
-
-
-CONFIGURATION = load_config()
diff --git a/dts/framework/dts.py b/dts/framework/dts.py
deleted file mode 100644
index 25d6942d81..0000000000
--- a/dts/framework/dts.py
+++ /dev/null
@@ -1,228 +0,0 @@
-# SPDX-License-Identifier: BSD-3-Clause
-# Copyright(c) 2010-2019 Intel Corporation
-# Copyright(c) 2022-2023 PANTHEON.tech s.r.o.
-# Copyright(c) 2022-2023 University of New Hampshire
-
-import sys
-
-from .config import (
- CONFIGURATION,
- BuildTargetConfiguration,
- ExecutionConfiguration,
- TestSuiteConfig,
-)
-from .exception import BlockingTestSuiteError
-from .logger import DTSLOG, getLogger
-from .test_result import BuildTargetResult, DTSResult, ExecutionResult, Result
-from .test_suite import get_test_suites
-from .testbed_model import SutNode, TGNode
-from .utils import check_dts_python_version
-
-dts_logger: DTSLOG = getLogger("DTSRunner")
-result: DTSResult = DTSResult(dts_logger)
-
-
-def run_all() -> None:
- """
- The main process of DTS. Runs all build targets in all executions from the main
- config file.
- """
- global dts_logger
- global result
-
- # check the python version of the server that run dts
- check_dts_python_version()
-
- sut_nodes: dict[str, SutNode] = {}
- tg_nodes: dict[str, TGNode] = {}
- try:
- # for all Execution sections
- for execution in CONFIGURATION.executions:
- sut_node = sut_nodes.get(execution.system_under_test_node.name)
- tg_node = tg_nodes.get(execution.traffic_generator_node.name)
-
- try:
- if not sut_node:
- sut_node = SutNode(execution.system_under_test_node)
- sut_nodes[sut_node.name] = sut_node
- if not tg_node:
- tg_node = TGNode(execution.traffic_generator_node)
- tg_nodes[tg_node.name] = tg_node
- result.update_setup(Result.PASS)
- except Exception as e:
- failed_node = execution.system_under_test_node.name
- if sut_node:
- failed_node = execution.traffic_generator_node.name
- dts_logger.exception(f"Creation of node {failed_node} failed.")
- result.update_setup(Result.FAIL, e)
-
- else:
- _run_execution(sut_node, tg_node, execution, result)
-
- except Exception as e:
- dts_logger.exception("An unexpected error has occurred.")
- result.add_error(e)
- raise
-
- finally:
- try:
- for node in (sut_nodes | tg_nodes).values():
- node.close()
- result.update_teardown(Result.PASS)
- except Exception as e:
- dts_logger.exception("Final cleanup of nodes failed.")
- result.update_teardown(Result.ERROR, e)
-
- # we need to put the sys.exit call outside the finally clause to make sure
- # that unexpected exceptions will propagate
- # in that case, the error that should be reported is the uncaught exception as
- # that is a severe error originating from the framework
- # at that point, we'll only have partial results which could be impacted by the
- # error causing the uncaught exception, making them uninterpretable
- _exit_dts()
-
-
-def _run_execution(
- sut_node: SutNode,
- tg_node: TGNode,
- execution: ExecutionConfiguration,
- result: DTSResult,
-) -> None:
- """
- Run the given execution. This involves running the execution setup as well as
- running all build targets in the given execution.
- """
- dts_logger.info(f"Running execution with SUT '{execution.system_under_test_node.name}'.")
- execution_result = result.add_execution(sut_node.config)
- execution_result.add_sut_info(sut_node.node_info)
-
- try:
- sut_node.set_up_execution(execution)
- execution_result.update_setup(Result.PASS)
- except Exception as e:
- dts_logger.exception("Execution setup failed.")
- execution_result.update_setup(Result.FAIL, e)
-
- else:
- for build_target in execution.build_targets:
- _run_build_target(sut_node, tg_node, build_target, execution, execution_result)
-
- finally:
- try:
- sut_node.tear_down_execution()
- execution_result.update_teardown(Result.PASS)
- except Exception as e:
- dts_logger.exception("Execution teardown failed.")
- execution_result.update_teardown(Result.FAIL, e)
-
-
-def _run_build_target(
- sut_node: SutNode,
- tg_node: TGNode,
- build_target: BuildTargetConfiguration,
- execution: ExecutionConfiguration,
- execution_result: ExecutionResult,
-) -> None:
- """
- Run the given build target.
- """
- dts_logger.info(f"Running build target '{build_target.name}'.")
- build_target_result = execution_result.add_build_target(build_target)
-
- try:
- sut_node.set_up_build_target(build_target)
- result.dpdk_version = sut_node.dpdk_version
- build_target_result.add_build_target_info(sut_node.get_build_target_info())
- build_target_result.update_setup(Result.PASS)
- except Exception as e:
- dts_logger.exception("Build target setup failed.")
- build_target_result.update_setup(Result.FAIL, e)
-
- else:
- _run_all_suites(sut_node, tg_node, execution, build_target_result)
-
- finally:
- try:
- sut_node.tear_down_build_target()
- build_target_result.update_teardown(Result.PASS)
- except Exception as e:
- dts_logger.exception("Build target teardown failed.")
- build_target_result.update_teardown(Result.FAIL, e)
-
-
-def _run_all_suites(
- sut_node: SutNode,
- tg_node: TGNode,
- execution: ExecutionConfiguration,
- build_target_result: BuildTargetResult,
-) -> None:
- """
- Use the given build_target to run execution's test suites
- with possibly only a subset of test cases.
- If no subset is specified, run all test cases.
- """
- end_build_target = False
- if not execution.skip_smoke_tests:
- execution.test_suites[:0] = [TestSuiteConfig.from_dict("smoke_tests")]
- for test_suite_config in execution.test_suites:
- try:
- _run_single_suite(sut_node, tg_node, execution, build_target_result, test_suite_config)
- except BlockingTestSuiteError as e:
- dts_logger.exception(
- f"An error occurred within {test_suite_config.test_suite}. Skipping build target."
- )
- result.add_error(e)
- end_build_target = True
- # if a blocking test failed and we need to bail out of suite executions
- if end_build_target:
- break
-
-
-def _run_single_suite(
- sut_node: SutNode,
- tg_node: TGNode,
- execution: ExecutionConfiguration,
- build_target_result: BuildTargetResult,
- test_suite_config: TestSuiteConfig,
-) -> None:
- """Runs a single test suite.
-
- Args:
- sut_node: Node to run tests on.
- execution: Execution the test case belongs to.
- build_target_result: Build target configuration test case is run on
- test_suite_config: Test suite configuration
-
- Raises:
- BlockingTestSuiteError: If a test suite that was marked as blocking fails.
- """
- try:
- full_suite_path = f"tests.TestSuite_{test_suite_config.test_suite}"
- test_suite_classes = get_test_suites(full_suite_path)
- suites_str = ", ".join((x.__name__ for x in test_suite_classes))
- dts_logger.debug(f"Found test suites '{suites_str}' in '{full_suite_path}'.")
- except Exception as e:
- dts_logger.exception("An error occurred when searching for test suites.")
- result.update_setup(Result.ERROR, e)
-
- else:
- for test_suite_class in test_suite_classes:
- test_suite = test_suite_class(
- sut_node,
- tg_node,
- test_suite_config.test_cases,
- execution.func,
- build_target_result,
- )
- test_suite.run()
-
-
-def _exit_dts() -> None:
- """
- Process all errors and exit with the proper exit code.
- """
- result.process()
-
- if dts_logger:
- dts_logger.info("DTS execution has ended.")
- sys.exit(result.get_return_code())
diff --git a/dts/framework/runner.py b/dts/framework/runner.py
new file mode 100644
index 0000000000..5b077c5805
--- /dev/null
+++ b/dts/framework/runner.py
@@ -0,0 +1,243 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(c) 2010-2019 Intel Corporation
+# Copyright(c) 2022-2023 PANTHEON.tech s.r.o.
+# Copyright(c) 2022-2023 University of New Hampshire
+
+import logging
+import sys
+
+from .config import (
+ BuildTargetConfiguration,
+ Configuration,
+ ExecutionConfiguration,
+ TestSuiteConfig,
+)
+from .exception import BlockingTestSuiteError
+from .logger import DTSLOG, getLogger
+from .test_result import BuildTargetResult, DTSResult, ExecutionResult, Result
+from .test_suite import get_test_suites
+from .testbed_model import SutNode, TGNode
+from .utils import check_dts_python_version
+
+
+class DTSRunner:
+ _logger: DTSLOG
+ _result: DTSResult
+ _configuration: Configuration
+
+ def __init__(self, configuration: Configuration):
+ self._logger = getLogger("DTSRunner")
+ self._result = DTSResult(self._logger)
+ self._configuration = configuration
+
+ def run(self):
+ """
+ The main process of DTS. Runs all build targets in all executions from the main
+ config file.
+ """
+ # check the python version of the server that run dts
+ check_dts_python_version()
+ sut_nodes: dict[str, SutNode] = {}
+ tg_nodes: dict[str, TGNode] = {}
+ try:
+ # for all Execution sections
+ for execution in self._configuration.executions:
+ sut_node = sut_nodes.get(execution.system_under_test_node.name)
+ tg_node = tg_nodes.get(execution.traffic_generator_node.name)
+
+ try:
+ if not sut_node:
+ sut_node = SutNode(execution.system_under_test_node)
+ sut_nodes[sut_node.name] = sut_node
+ if not tg_node:
+ tg_node = TGNode(execution.traffic_generator_node)
+ tg_nodes[tg_node.name] = tg_node
+ self._result.update_setup(Result.PASS)
+ except Exception as e:
+ failed_node = execution.system_under_test_node.name
+ if sut_node:
+ failed_node = execution.traffic_generator_node.name
+ self._logger.exception(
+ f"The Creation of node {failed_node} failed."
+ )
+ self._result.update_setup(Result.FAIL, e)
+
+ else:
+ self._run_execution(sut_node, tg_node, execution)
+
+ except Exception as e:
+ self._logger.exception("An unexpected error has occurred.")
+ self._result.add_error(e)
+ raise
+
+ finally:
+ try:
+ for node in (sut_nodes | tg_nodes).values():
+ node.close()
+ self._result.update_teardown(Result.PASS)
+ except Exception as e:
+ self._logger.exception("The final cleanup of nodes failed.")
+ self._result.update_teardown(Result.ERROR, e)
+
+ # we need to put the sys.exit call outside the finally clause to make sure
+ # that unexpected exceptions will propagate
+ # in that case, the error that should be reported is the uncaught exception as
+ # that is a severe error originating from the framework
+ # at that point, we'll only have partial results which could be impacted by the
+ # error causing the uncaught exception, making them uninterpretable
+ self._exit_dts()
+
+ def _run_execution(
+ self,
+ sut_node: SutNode,
+ tg_node: TGNode,
+ execution: ExecutionConfiguration,
+ ) -> None:
+ """
+ Run the given execution. This involves running the execution setup as well as
+ running all build targets in the given execution.
+ """
+ self._logger.info(
+ f"Running execution with SUT '{execution.system_under_test_node.name}'."
+ )
+ execution_result = self._result.add_execution(sut_node.config)
+ execution_result.add_sut_info(sut_node.node_info)
+
+ try:
+ sut_node.set_up_execution(execution)
+ execution_result.update_setup(Result.PASS)
+ except Exception as e:
+ self._logger.exception("Execution setup failed.")
+ execution_result.update_setup(Result.FAIL, e)
+
+ else:
+ for build_target in execution.build_targets:
+ self._run_build_target(
+ sut_node, tg_node, build_target, execution, execution_result
+ )
+
+ finally:
+ try:
+ sut_node.tear_down_execution()
+ execution_result.update_teardown(Result.PASS)
+ except Exception as e:
+ self._logger.exception("Execution teardown failed.")
+ execution_result.update_teardown(Result.FAIL, e)
+
+ def _run_build_target(
+ self,
+ sut_node: SutNode,
+ tg_node: TGNode,
+ build_target: BuildTargetConfiguration,
+ execution: ExecutionConfiguration,
+ execution_result: ExecutionResult,
+ ) -> None:
+ """
+ Run the given build target.
+ """
+ self._logger.info(f"Running build target '{build_target.name}'.")
+ build_target_result = execution_result.add_build_target(build_target)
+
+ try:
+ sut_node.set_up_build_target(build_target)
+ self._result.dpdk_version = sut_node.dpdk_version
+ build_target_result.add_build_target_info(sut_node.get_build_target_info())
+ build_target_result.update_setup(Result.PASS)
+ except Exception as e:
+ self._logger.exception("Build target setup failed.")
+ build_target_result.update_setup(Result.FAIL, e)
+
+ else:
+ self._run_all_suites(sut_node, tg_node, execution, build_target_result)
+
+ finally:
+ try:
+ sut_node.tear_down_build_target()
+ build_target_result.update_teardown(Result.PASS)
+ except Exception as e:
+ self._logger.exception("Build target teardown failed.")
+ build_target_result.update_teardown(Result.FAIL, e)
+
+ def _run_all_suites(
+ self,
+ sut_node: SutNode,
+ tg_node: TGNode,
+ execution: ExecutionConfiguration,
+ build_target_result: BuildTargetResult,
+ ) -> None:
+ """
+ Use the given build_target to run execution's test suites
+ with possibly only a subset of test cases.
+ If no subset is specified, run all test cases.
+ """
+ end_build_target = False
+ if not execution.skip_smoke_tests:
+ execution.test_suites[:0] = [TestSuiteConfig.from_dict("smoke_tests")]
+ for test_suite_config in execution.test_suites:
+ try:
+ self._run_single_suite(
+ sut_node, tg_node, execution, build_target_result, test_suite_config
+ )
+ except BlockingTestSuiteError as e:
+ self._logger.exception(
+ f"An error occurred within {test_suite_config.test_suite}. "
+ "Skipping build target..."
+ )
+ self._result.add_error(e)
+ end_build_target = True
+ # if a blocking test failed and we need to bail out of suite executions
+ if end_build_target:
+ break
+
+ def _run_single_suite(
+ self,
+ sut_node: SutNode,
+ tg_node: TGNode,
+ execution: ExecutionConfiguration,
+ build_target_result: BuildTargetResult,
+ test_suite_config: TestSuiteConfig,
+ ) -> None:
+ """Runs a single test suite.
+
+ Args:
+ sut_node: Node to run tests on.
+ execution: Execution the test case belongs to.
+ build_target_result: Build target configuration test case is run on
+ test_suite_config: Test suite configuration
+
+ Raises:
+ BlockingTestSuiteError: If a test suite that was marked as blocking fails.
+ """
+ try:
+ full_suite_path = f"tests.TestSuite_{test_suite_config.test_suite}"
+ test_suite_classes = get_test_suites(full_suite_path)
+ suites_str = ", ".join((x.__name__ for x in test_suite_classes))
+ self._logger.debug(
+ f"Found test suites '{suites_str}' in '{full_suite_path}'."
+ )
+ except Exception as e:
+ self._logger.exception("An error occurred when searching for test suites.")
+ self._result.update_setup(Result.ERROR, e)
+
+ else:
+ for test_suite_class in test_suite_classes:
+ test_suite = test_suite_class(
+ sut_node,
+ tg_node,
+ test_suite_config.test_cases,
+ execution.func,
+ build_target_result,
+ )
+ test_suite.run()
+
+ def _exit_dts(self) -> None:
+ """
+ Process all errors and exit with the proper exit code.
+ """
+ self._result.process()
+
+ if self._logger:
+ self._logger.info("DTS execution has ended.")
+
+ logging.shutdown()
+ sys.exit(self._result.get_return_code())
diff --git a/dts/main.py b/dts/main.py
index 43311fa847..879ce5cb89 100755
--- a/dts/main.py
+++ b/dts/main.py
@@ -10,11 +10,13 @@
import logging
-from framework import dts
+from framework.config import load_config
+from framework.runner import DTSRunner
def main() -> None:
- dts.run_all()
+ dts = DTSRunner(configuration=load_config())
+ dts.run()
# Main program begins here
--
2.34.1
next prev parent reply other threads:[~2023-12-20 10:33 UTC|newest]
Thread overview: 44+ messages / expand[flat|nested] mbox.gz Atom feed top
2023-12-20 10:33 [RFC PATCH v1 0/5] test case blocking and logging Juraj Linkeš
2023-12-20 10:33 ` Juraj Linkeš [this message]
2023-12-20 10:33 ` [RFC PATCH v1 2/5] dts: move test suite execution logic to DTSRunner Juraj Linkeš
2023-12-20 10:33 ` [RFC PATCH v1 3/5] dts: process test suites at the beginning of run Juraj Linkeš
2023-12-20 10:33 ` [RFC PATCH v1 4/5] dts: block all testcases when earlier setup fails Juraj Linkeš
2023-12-20 10:33 ` [RFC PATCH v1 5/5] dts: refactor logging configuration Juraj Linkeš
2024-01-08 18:47 ` [RFC PATCH v1 0/5] test case blocking and logging Jeremy Spewock
2024-02-06 14:57 ` [PATCH v2 0/7] " Juraj Linkeš
2024-02-06 14:57 ` [PATCH v2 1/7] dts: convert dts.py methods to class Juraj Linkeš
2024-02-06 14:57 ` [PATCH v2 2/7] dts: move test suite execution logic to DTSRunner Juraj Linkeš
2024-02-06 14:57 ` [PATCH v2 3/7] dts: filter test suites in executions Juraj Linkeš
2024-02-12 16:44 ` Jeremy Spewock
2024-02-14 9:55 ` Juraj Linkeš
2024-02-06 14:57 ` [PATCH v2 4/7] dts: reorganize test result Juraj Linkeš
2024-02-06 14:57 ` [PATCH v2 5/7] dts: block all test cases when earlier setup fails Juraj Linkeš
2024-02-06 14:57 ` [PATCH v2 6/7] dts: refactor logging configuration Juraj Linkeš
2024-02-12 16:45 ` Jeremy Spewock
2024-02-14 7:49 ` Juraj Linkeš
2024-02-14 16:51 ` Jeremy Spewock
2024-02-06 14:57 ` [PATCH v2 7/7] dts: improve test suite and case filtering Juraj Linkeš
2024-02-23 7:54 ` [PATCH v3 0/7] test case blocking and logging Juraj Linkeš
2024-02-23 7:54 ` [PATCH v3 1/7] dts: convert dts.py methods to class Juraj Linkeš
2024-02-23 7:54 ` [PATCH v3 2/7] dts: move test suite execution logic to DTSRunner Juraj Linkeš
2024-02-23 7:54 ` [PATCH v3 3/7] dts: filter test suites in executions Juraj Linkeš
2024-02-27 21:21 ` Jeremy Spewock
2024-02-28 9:16 ` Juraj Linkeš
2024-02-23 7:54 ` [PATCH v3 4/7] dts: reorganize test result Juraj Linkeš
2024-02-23 7:55 ` [PATCH v3 5/7] dts: block all test cases when earlier setup fails Juraj Linkeš
2024-02-23 7:55 ` [PATCH v3 6/7] dts: refactor logging configuration Juraj Linkeš
2024-02-23 7:55 ` [PATCH v3 7/7] dts: improve test suite and case filtering Juraj Linkeš
2024-02-27 21:24 ` [PATCH v3 0/7] test case blocking and logging Jeremy Spewock
2024-03-01 10:55 ` [PATCH v4 " Juraj Linkeš
2024-03-01 10:55 ` [PATCH v4 1/7] dts: convert dts.py methods to class Juraj Linkeš
2024-03-01 10:55 ` [PATCH v4 2/7] dts: move test suite execution logic to DTSRunner Juraj Linkeš
2024-03-01 10:55 ` [PATCH v4 3/7] dts: filter test suites in executions Juraj Linkeš
2024-03-01 10:55 ` [PATCH v4 4/7] dts: reorganize test result Juraj Linkeš
2024-03-01 10:55 ` [PATCH v4 5/7] dts: block all test cases when earlier setup fails Juraj Linkeš
2024-03-01 10:55 ` [PATCH v4 6/7] dts: refactor logging configuration Juraj Linkeš
2024-03-01 10:55 ` [PATCH v4 7/7] dts: improve test suite and case filtering Juraj Linkeš
2024-03-01 17:41 ` Jeremy Spewock
2024-03-01 17:43 ` Jeremy Spewock
2024-03-07 11:04 ` Thomas Monjalon
2024-03-01 16:11 ` [PATCH v4 0/7] test case blocking and logging Patrick Robb
2024-03-07 14:55 ` Thomas Monjalon
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20231220103331.60888-2-juraj.linkes@pantheon.tech \
--to=juraj.linkes@pantheon.tech \
--cc=Honnappa.Nagarahalli@arm.com \
--cc=Luca.Vizzarro@arm.com \
--cc=dev@dpdk.org \
--cc=jspewock@iol.unh.edu \
--cc=paul.szczepanek@arm.com \
--cc=probb@iol.unh.edu \
--cc=thomas@monjalon.net \
--cc=yoan.picchi@foss.arm.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).