Add a facility to create and ues a temporary runtime directory on the
remote nodes for each test run.

This requires adding setup and teardown steps for Node. To ensure that
setup is not performed twice in case of a loopback setup where the same
node is used both as SUT and TG, an internal state about the setup is
held.

Signed-off-by: Luca Vizzarro <luca.vizza...@arm.com>
Reviewed-by: Paul Szczepanek <paul.szczepa...@arm.com>
---
 dts/framework/test_run.py                    |  4 +++
 dts/framework/testbed_model/node.py          | 31 +++++++++++++++++++-
 dts/framework/testbed_model/os_session.py    | 12 ++++++++
 dts/framework/testbed_model/posix_session.py |  5 ++++
 4 files changed, 51 insertions(+), 1 deletion(-)

diff --git a/dts/framework/test_run.py b/dts/framework/test_run.py
index 9f37c4bb6c..69cc76bc25 100644
--- a/dts/framework/test_run.py
+++ b/dts/framework/test_run.py
@@ -333,6 +333,8 @@ def next(self) -> State | None:
         test_run.init_random_seed()
         test_run.remaining_tests = deque(test_run.selected_tests)
 
+        test_run.ctx.sut_node.setup()
+        test_run.ctx.tg_node.setup()
         test_run.ctx.dpdk.setup(test_run.ctx.topology.sut_ports)
 
         self.result.ports = test_run.ctx.topology.sut_ports + 
test_run.ctx.topology.tg_ports
@@ -417,6 +419,8 @@ def description(self) -> str:
     def next(self) -> State | None:
         """Next state."""
         self.test_run.ctx.dpdk.teardown(self.test_run.ctx.topology.sut_ports)
+        self.test_run.ctx.tg_node.teardown()
+        self.test_run.ctx.sut_node.teardown()
         self.result.update_teardown(Result.PASS)
         return None
 
diff --git a/dts/framework/testbed_model/node.py 
b/dts/framework/testbed_model/node.py
index be1b4ac2ac..e6737cd173 100644
--- a/dts/framework/testbed_model/node.py
+++ b/dts/framework/testbed_model/node.py
@@ -14,12 +14,13 @@
 """
 
 from functools import cached_property
+from pathlib import PurePath
 
 from framework.config.node import (
     OS,
     NodeConfiguration,
 )
-from framework.exception import ConfigurationError
+from framework.exception import ConfigurationError, InternalError
 from framework.logger import DTSLogger, get_dts_logger
 
 from .cpu import Architecture, LogicalCore
@@ -56,6 +57,7 @@ class Node:
     _other_sessions: list[OSSession]
     _node_info: OSSessionInfo | None
     _compiler_version: str | None
+    _setup: bool
 
     def __init__(self, node_config: NodeConfiguration):
         """Connect to the node and gather info during initialization.
@@ -77,9 +79,36 @@ def __init__(self, node_config: NodeConfiguration):
         self._logger.info(f"Connected to node: {self.name}")
         self._get_remote_cpus()
         self._other_sessions = []
+        self._setup = False
         self.ports = [Port(self, port_config) for port_config in 
self.config.ports]
         self._logger.info(f"Created node: {self.name}")
 
+    def setup(self) -> None:
+        """Node setup."""
+        if self._setup:
+            return
+
+        self.tmp_dir = self.main_session.create_tmp_dir()
+        self._setup = True
+
+    def teardown(self) -> None:
+        """Node teardown."""
+        if not self._setup:
+            return
+
+        self.main_session.remove_remote_dir(self.tmp_dir)
+        del self.tmp_dir
+        self._setup = False
+
+    @cached_property
+    def tmp_dir(self) -> PurePath:
+        """Path to the temporary directory.
+
+        Raises:
+            InternalError: If called before the node has been setup.
+        """
+        raise InternalError("Temporary directory requested before setup.")
+
     @cached_property
     def ports_by_name(self) -> dict[str, Port]:
         """Ports mapped by the name assigned at configuration."""
diff --git a/dts/framework/testbed_model/os_session.py 
b/dts/framework/testbed_model/os_session.py
index 3c7b2a4f47..354c607357 100644
--- a/dts/framework/testbed_model/os_session.py
+++ b/dts/framework/testbed_model/os_session.py
@@ -195,6 +195,18 @@ def remote_path_exists(self, remote_path: str | PurePath) 
-> bool:
             :data:`True` if the path exists, :data:`False` otherwise.
         """
 
+    @abstractmethod
+    def create_tmp_dir(self, template: str = "dts.XXXXX") -> PurePath:
+        """Create a temporary directory on the remote node.
+
+        Args:
+            template: The template to use for the name of the directory. "X"s 
are treated
+                as placeholder.
+
+        Returns:
+            The path to the created directory.
+        """
+
     @abstractmethod
     def copy_from(self, source_file: str | PurePath, destination_dir: str | 
Path) -> None:
         """Copy a file from the remote node to the local filesystem.
diff --git a/dts/framework/testbed_model/posix_session.py 
b/dts/framework/testbed_model/posix_session.py
index eab08a90ce..2d2701e1cf 100644
--- a/dts/framework/testbed_model/posix_session.py
+++ b/dts/framework/testbed_model/posix_session.py
@@ -96,6 +96,11 @@ def remote_path_exists(self, remote_path: str | PurePath) -> 
bool:
         result = self.send_command(f"test -e {remote_path}")
         return not result.return_code
 
+    def create_tmp_dir(self, template: str = "dts.XXXXX") -> PurePath:
+        """Overrides :meth:`~.os_session.OSSession.create_tmp_dir`."""
+        result = self.send_command(f'mktemp -dp "" {template}')
+        return PurePath(result.stdout)
+
     def copy_from(self, source_file: str | PurePath, destination_dir: str | 
Path) -> None:
         """Overrides :meth:`~.os_session.OSSession.copy_from`."""
         self.remote_session.copy_from(source_file, destination_dir)
-- 
2.43.0

Reply via email to