blob: 889cce0b6db3e6250e34bfb33c787e8ca5ed7316 [file]
#
# Copyright (c) 2026 Project CHIP Authors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
"""
Handles linux-specific functionality for running test cases
"""
import contextlib
import logging
import os
import shlex
import shutil
import subprocess
from typing import BinaryIO
from chiptest.concurrency.worker import WorkerProcess
from chiptest.runner import Executor, LogPipe, SubprocessInfo
from chiptest.test_definition import TEST_THREAD_DATASET
from python_path import PythonPath
log = logging.getLogger(__name__)
root_dir = os.path.dirname(
os.path.dirname(
os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
with PythonPath(os.path.join(root_dir, 'src/python_testing/matter_testing_infrastructure'), relative_to=__file__):
from matter.testing.commissioning_types import CommissioningMethod
from matter.testing.linux import (BluetoothMock, DBusTestSystemBus, IsolatedNetworkNamespace, ThreadBorderRouter,
WpaSupplicantMock, ensure_namespace_availability, ensure_private_state)
__all__ = [
"ensure_namespace_availability",
"ensure_private_state",
"BluetoothMock",
"DBusTestSystemBus",
"IsolatedNetworkNamespace",
"LinuxNamespacedExecutor",
"ThreadBorderRouter",
"WpaSupplicantMock"
]
class LinuxNamespacedExecutor(Executor):
def __init__(self, ns: IsolatedNetworkNamespace):
super().__init__()
self.ns = ns
def run(self, subproc: SubprocessInfo, stdin: BinaryIO | None = None, stdout: BinaryIO | LogPipe | None = None,
stderr: BinaryIO | LogPipe | None = None):
try:
subprocess_ns = self.ns.netns_for_subprocess_kind(subproc.kind)
wrapped = subproc.wrap_with(*subprocess_ns.netns_cmd_wrapper)
except ValueError as e:
log.warning("%s", e)
wrapped = subproc
return super().run(wrapped, stdin=stdin, stdout=stdout, stderr=stderr)
class LinuxWorkerProcess(WorkerProcess):
"""Linux implementation of the worker process."""
def _platform_init(self, exit_stack: contextlib.ExitStack) -> LinuxNamespacedExecutor:
log.debug("Initializing Linux test executor")
# Create a virtual /tmp.
tmp_dir_default = self._config.tmp_dir_default
tmp_dir = self._config.tmp_dir_worker_base / str(self._config.id)
if self._config.tmp_dir_clear:
shutil.rmtree(tmp_dir)
tmp_dir.mkdir(parents=True, exist_ok=True)
# Check if the directory is empty.
with contextlib.suppress(StopIteration):
next(tmp_dir.iterdir())
log.warning("Worker virtual %s directory (%s) is not empty. The worker will reuse the state from the last run",
tmp_dir_default, tmp_dir)
log.info("Remounting %s as %s for the worker", tmp_dir, tmp_dir_default)
if subprocess.run(["mount", "-o", "bind", str(tmp_dir), str(tmp_dir_default)]).returncode != 0:
raise RuntimeError(f"Failed to mount a virtual {tmp_dir_default}")
commissioning_method = self._config.commissioning_method
self.net_ns: IsolatedNetworkNamespace = exit_stack.enter_context(IsolatedNetworkNamespace(
index=self._config.id,
# Do not bring up the app interface link automatically when doing BLE-WiFi commissioning.
app_link_up=not commissioning_method.wifi_required,
add_ula=not commissioning_method.thread_required,
# Change the app link name so the interface will be recognized as WiFi or Ethernet depending on the commissioning
# method used.
app_link_name=commissioning_method.app_link_name, tool_link_name=commissioning_method.tool_link_name))
self.mgmt_ns_wrapper = shlex.join(self.net_ns.mgmt_ns.netns_cmd_wrapper)
match commissioning_method:
case CommissioningMethod.BLE_WIFI:
exit_stack.enter_context(DBusTestSystemBus())
exit_stack.enter_context(BluetoothMock())
exit_stack.enter_context(WpaSupplicantMock([commissioning_method.app_link_name],
"MatterAP", "MatterAPPassword", self.net_ns))
case CommissioningMethod.BLE_THREAD:
exit_stack.enter_context(DBusTestSystemBus())
exit_stack.enter_context(BluetoothMock())
exit_stack.enter_context(ThreadBorderRouter(TEST_THREAD_DATASET, self.net_ns))
case CommissioningMethod.THREAD_MESHCOP:
exit_stack.enter_context(tbr := ThreadBorderRouter(TEST_THREAD_DATASET, self.net_ns))
self.thread_ba_host = tbr.get_border_agent_host()
self.thread_ba_port = tbr.get_border_agent_port()
case CommissioningMethod.WIFIPAF_WIFI:
exit_stack.enter_context(DBusTestSystemBus())
exit_stack.enter_context(WpaSupplicantMock(
[commissioning_method.app_link_name, commissioning_method.tool_link_name],
"MatterAP", "MatterAPPassword", self.net_ns))
return exit_stack.enter_context(LinuxNamespacedExecutor(self.net_ns))