Skip to content

[Camera] Add CameraAppTestSuite for Matter Camera TCs #210

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@
CHIP_TOOL_EXE = "./chip-tool"
CHIP_TOOL_ARG_PAA_CERTS_PATH = "--paa-trust-store-path"

CHIP_CAMERA_CONTROLLER_EXE = "./chip-camera-controller"

# Chip App Parameters
CHIP_APP_EXE = "./chip-app1"

Expand All @@ -49,6 +51,7 @@ class UnsupportedChipServerType(Exception):
class ChipServerType(str, Enum):
CHIP_TOOL = "chip-tool"
CHIP_APP = "chip-app"
CHIP_CAMERA_CONTROLLER = "chip-camera-controller"


class ChipServer(metaclass=Singleton):
Expand Down Expand Up @@ -119,7 +122,10 @@ async def start(
self.__use_paa_certs = use_paa_certs
self.__server_type = server_type

if server_type == ChipServerType.CHIP_TOOL:
if server_type == ChipServerType.CHIP_CAMERA_CONTROLLER:
prefix = CHIP_CAMERA_CONTROLLER_EXE
command = ["interactive", "server"]
elif server_type == ChipServerType.CHIP_TOOL:
prefix = CHIP_TOOL_EXE
command = ["interactive", "server"]
elif server_type == ChipServerType.CHIP_APP:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -228,7 +228,7 @@
pics_path = f"{PICS_FILE_PATH}"
self.logger.info(f"Using PICS file: {pics_path}")

if server_type == ChipServerType.CHIP_TOOL:
if server_type == ChipServerType.CHIP_TOOL or server_type == ChipServerType.CHIP_CAMERA_CONTROLLER:

Check failure on line 231 in test_collections/matter/sdk_tests/support/yaml_tests/matter_yaml_runner.py

View workflow job for this annotation

GitHub Actions / Flake8

test_collections/matter/sdk_tests/support/yaml_tests/matter_yaml_runner.py#L231

Line too long (107 > 88 characters) (E501)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if server_type == ChipServerType.CHIP_TOOL or server_type == ChipServerType.CHIP_CAMERA_CONTROLLER:
if (
server_type == ChipServerType.CHIP_TOOL
or server_type == ChipServerType.CHIP_CAMERA_CONTROLLER
):

test_path = f"{YAML_TESTS_PATH}/{test_id}.yaml"
else:
test_path = f"{YAML_TESTS_PATH}/{test_id}_Simulated.yaml"
Expand All @@ -238,7 +238,8 @@
[test_path], parser_config, test_parser_hooks
)

if server_type == ChipServerType.CHIP_TOOL:
#Reuse chip-tool adapter for camera-controller

Check failure on line 241 in test_collections/matter/sdk_tests/support/yaml_tests/matter_yaml_runner.py

View workflow job for this annotation

GitHub Actions / Flake8

test_collections/matter/sdk_tests/support/yaml_tests/matter_yaml_runner.py#L241

Block comment should start with '# ' (E265)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
#Reuse chip-tool adapter for camera-controller
# Reuse chip-tool adapter for camera-controller

if server_type == ChipServerType.CHIP_TOOL or server_type == ChipServerType.CHIP_CAMERA_CONTROLLER:

Check failure on line 242 in test_collections/matter/sdk_tests/support/yaml_tests/matter_yaml_runner.py

View workflow job for this annotation

GitHub Actions / Flake8

test_collections/matter/sdk_tests/support/yaml_tests/matter_yaml_runner.py#L242

Line too long (107 > 88 characters) (E501)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if server_type == ChipServerType.CHIP_TOOL or server_type == ChipServerType.CHIP_CAMERA_CONTROLLER:
if (
server_type == ChipServerType.CHIP_TOOL
or server_type == ChipServerType.CHIP_CAMERA_CONTROLLER
):

adapter = ChipToolAdapter.Adapter(parser_config.definitions)
elif server_type == ChipServerType.CHIP_APP:
adapter = ChipAppAdapter.Adapter(parser_config.definitions)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,10 @@ async def setup(self) -> None:
self.runner.reset_pics_state()

self.__dut_commissioned_successfully = False
if self.server_type == ChipServerType.CHIP_TOOL:
if (
self.server_type == ChipServerType.CHIP_TOOL
or self.server_type == ChipServerType.CHIP_CAMERA_CONTROLLER
):
logger.info("Commission DUT")
user_response = await prompt_for_commissioning_mode(
self, logger, None, self.cancel
Expand Down Expand Up @@ -198,7 +201,10 @@ async def cleanup(self) -> None:
# Only unpair if commissioning was successfull during setup
if self.__dut_commissioned_successfully:
# Unpair is not applicable for simulated apps case
if self.server_type == ChipServerType.CHIP_TOOL:
if (
self.server_type == ChipServerType.CHIP_TOOL
or self.server_type == ChipServerType.CHIP_CAMERA_CONTROLLER
):
logger.info("Unpairing DUT from server")
await self.runner.unpair()
elif self.server_type == ChipServerType.CHIP_APP:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
from ...chip.chip_server import ChipServerType
from ...models.matter_test_models import MatterTestStep, MatterTestType
from ...yaml_tests.models.chip_test import ChipManualPromptTest, ChipTest
from .test_suite import SuiteType
from .yaml_test_models import YamlTest

# Custom type variable used to annotate the factory method in YamlTestCase.
Expand Down Expand Up @@ -81,11 +82,18 @@
if test.type == MatterTestType.MANUAL:
case_class = YamlManualTestCase
elif test.type == MatterTestType.SEMI_AUTOMATED:
case_class = YamlSemiAutomatedChipTestCase
if test.suite_type == SuiteType.CAMERA_AUTOMATED:
case_class = YamlCameraSemiAutomatedChipTestCase
else:
case_class = YamlSemiAutomatedChipTestCase

elif test.type == MatterTestType.SIMULATED:
case_class = YamlSimulatedTestCase
else: # Automated
case_class = YamlChipTestCase
if test.suite_type == SuiteType.CAMERA_AUTOMATED:
case_class = YamlCameraChipTestCase
else:
case_class = YamlChipTestCase

return case_class.__class_factory(test=test, yaml_version=yaml_version)

Expand Down Expand Up @@ -162,7 +170,8 @@
Disabled steps are ignored.
(Such tests will be marked as 'Steps Disabled' elsewhere)

UserPrompt are special cases that will prompt test operator for input.
UserPrompt, PromptWithResponse or VerifyVideoStream are special cases that will
prompt test operator for input.
"""
if yaml_step.disabled:
test_engine_logger.info(
Expand All @@ -171,7 +180,11 @@
return

step = TestStep(yaml_step.label)
if yaml_step.command == "UserPrompt":
if yaml_step.command in [
"UserPrompt",
"PromptWithResponse",
"VerifyVideoStream",
]:
step = ManualVerificationTestStep(
name=yaml_step.label,
verification=yaml_step.verification,
Expand Down Expand Up @@ -204,6 +217,22 @@
for step in self.yaml_test.steps:
self._append_automated_test_step(step)

class YamlCameraChipTestCase(YamlTestCase, ChipTest):

Check failure on line 220 in test_collections/matter/sdk_tests/support/yaml_tests/models/test_case.py

View workflow job for this annotation

GitHub Actions / Flake8

test_collections/matter/sdk_tests/support/yaml_tests/models/test_case.py#L220

Expected 2 blank lines, found 1 (E302)
Comment on lines 219 to +220
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
class YamlCameraChipTestCase(YamlTestCase, ChipTest):
class YamlCameraChipTestCase(YamlTestCase, ChipTest):

"""Automated test cases using chip-camera-controller."""

server_type = ChipServerType.CHIP_CAMERA_CONTROLLER

def create_test_steps(self) -> None:
self.test_steps = [TestStep("Start chip-camera-controller test")]
for step in self.yaml_test.steps:
self._append_automated_test_step(step)


class YamlCameraSemiAutomatedChipTestCase(YamlChipTestCase, ChipManualPromptTest):
"""Camera Semi-Automated test cases, need special step for users to attach logs
for manual steps, so inheriting from ChipManualPromptTest.
"""


class YamlSemiAutomatedChipTestCase(YamlChipTestCase, ChipManualPromptTest):
"""Semi-Automated test cases, need special step for users to attach logs
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
from typing import Type
from typing import Optional, Type

from app.test_engine.models.test_declarations import (
TestCaseDeclaration,
Expand Down Expand Up @@ -53,11 +53,13 @@ class YamlCaseDeclaration(TestCaseDeclaration):
"""Direct initialization for YAML Test Case."""

class_ref: Type[YamlTestCase]
suite_type: Optional[SuiteType] = None

def __init__(self, test: YamlTest, yaml_version: str) -> None:
super().__init__(
YamlTestCase.class_factory(test=test, yaml_version=yaml_version)
)
self.suite_type = test.suite_type

@property
def test_type(self) -> MatterTestType:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
SIMULATED = 1
AUTOMATED = 2
MANUAL = 3
CAMERA_AUTOMATED = 4


# Custom Type variable used to annotate the factory methods of classmethod.
Expand Down Expand Up @@ -69,6 +70,8 @@
suite_class = ManualYamlTestSuite
elif suite_type == SuiteType.SIMULATED:
suite_class = SimulatedYamlTestSuite
elif suite_type == SuiteType.CAMERA_AUTOMATED:
suite_class = ChipCameraYamlTestSuite
elif suite_type == SuiteType.AUTOMATED:
suite_class = ChipYamlTestSuite

Expand Down Expand Up @@ -111,6 +114,15 @@
await YamlTestSuite.setup(self)
await ChipSuite.setup(self)

class ChipCameraYamlTestSuite(YamlTestSuite, ChipSuite):

Check failure on line 117 in test_collections/matter/sdk_tests/support/yaml_tests/models/test_suite.py

View workflow job for this annotation

GitHub Actions / Flake8

test_collections/matter/sdk_tests/support/yaml_tests/models/test_suite.py#L117

Expected 2 blank lines, found 1 (E302)
Comment on lines 116 to +117
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
class ChipCameraYamlTestSuite(YamlTestSuite, ChipSuite):
class ChipCameraYamlTestSuite(YamlTestSuite, ChipSuite):

server_type = ChipServerType.CHIP_CAMERA_CONTROLLER

async def setup(self) -> None:
"""Due top multi inheritance, we need to call setup on both super classes."""
self.server_type = ChipServerType.CHIP_CAMERA_CONTROLLER
await YamlTestSuite.setup(self)
await ChipSuite.setup(self)


class SimulatedYamlTestSuite(ChipYamlTestSuite):
server_type = ChipServerType.CHIP_APP
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,20 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
from typing import Any
from typing import Any, Optional

from pydantic_yaml import YamlModelMixin

from ...models.matter_test_models import MatterTest
from .test_suite import SuiteType

###
# This file declares YAML models that are used to parse the YAML Test Cases.
###


class YamlTest(YamlModelMixin, MatterTest):
suite_type: Optional[SuiteType] = None

def __init__(self, **kwargs: Any) -> None:
super().__init__(steps=kwargs["tests"], **kwargs)
Original file line number Diff line number Diff line change
Expand Up @@ -13,20 +13,22 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
import re
from pathlib import Path

from loguru import logger
from pydantic import ValidationError

from ...models.matter_test_models import MatterTestType
from .test_suite import SuiteType
from .yaml_test_models import YamlTest


class YamlParserException(Exception):
"""Raised when an error occurs during the parser of yaml file."""


def _test_type(test: YamlTest) -> MatterTestType:
def _get_types(test: YamlTest) -> tuple[MatterTestType, SuiteType]:
Copy link
Contributor

@antonio-amjr antonio-amjr Apr 23, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please, also change the method name in the Unit Test file test_yaml_parser.py everywhere it's used.

If possible, please run the Unit Tests to verify if anything else is missing or an issue is encountered.

"""Determine the type of a test based on the parsed yaml.

This is mainly determined by the number of disabled test steps.
Expand All @@ -35,27 +37,46 @@ def _test_type(test: YamlTest) -> MatterTestType:
test (YamlTest): parsed yaml model

Returns:
TestType:
- Manual: All steps disabled
- Semi-Automated: some steps are disabled
- Automated: no disabled steps
- Simulated: Tests where file name have "Simulated"
tuple[MatterTestType, SuiteType]:
SuiteType:
- Manual: All steps disabled
- Semi-Automated: some steps are disabled
- Automated: no disabled steps
- Simulated: Tests where file name have "Simulated"
SuiteType:
- SIMULATED: Simulated Test Suite
- AUTOMATED: Automated Test Suite
- MANUAL: Manual Test Suite
- CAMERA_AUTOMATED: Automated Camera Test Suite
"""
camera_test_pattern = r"\[TC-WEBRTC-\d+\.\d+\]"

if test.path is not None and "Simulated" in str(test.path):
return MatterTestType.SIMULATED
return MatterTestType.SIMULATED, SuiteType.SIMULATED

steps = test.steps

# If all disabled:
if all(s.disabled is True for s in steps):
return MatterTestType.MANUAL

# if any step has a UserPrompt, categorize as semi-automated
if any(s.command == "UserPrompt" for s in steps):
return MatterTestType.SEMI_AUTOMATED

# Otherwise Automated
return MatterTestType.AUTOMATED
return MatterTestType.MANUAL, SuiteType.MANUAL

# if any step has a UserPrompt, PromptWithResponse or VerifyVideoStream command,
# categorize as semi-automated
if any(
s.command in ["UserPrompt", "PromptWithResponse", "VerifyVideoStream"]
for s in steps
):
if re.search(camera_test_pattern, test.name):
return MatterTestType.SEMI_AUTOMATED, SuiteType.CAMERA_AUTOMATED
else:
return MatterTestType.SEMI_AUTOMATED, SuiteType.AUTOMATED

# Otherwise
# If test case is camera related, then return SuiteType.CAMERA_AUTOMATED
if re.search(camera_test_pattern, test.name):
return MatterTestType.AUTOMATED, SuiteType.CAMERA_AUTOMATED
else:
return MatterTestType.AUTOMATED, SuiteType.AUTOMATED


def parse_yaml_test(path: Path) -> YamlTest:
Expand All @@ -68,7 +89,9 @@ def parse_yaml_test(path: Path) -> YamlTest:
yaml_str = file.read()
test = YamlTest.parse_raw(yaml_str, proto="yaml")
test.path = path
test.type = _test_type(test)
test_type, suite_type = _get_types(test)
test.type = test_type
test.suite_type = suite_type
except ValidationError as e:
logger.error(str(e))
raise YamlParserException(f"The YAML file {path} is invalid") from e
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,11 @@ def _init_test_suites(yaml_version: str) -> dict[SuiteType, YamlSuiteDeclaration
suite_type=SuiteType.SIMULATED,
version=yaml_version,
),
SuiteType.CAMERA_AUTOMATED: YamlSuiteDeclaration(
name="CameraAppTestSuite",
suite_type=SuiteType.CAMERA_AUTOMATED,
version=yaml_version,
),
}


Expand All @@ -80,10 +85,11 @@ def _parse_yaml_to_test_case_declaration(
def _parse_all_yaml(
yaml_files: list[Path], yaml_version: str
) -> list[YamlSuiteDeclaration]:
"""Parse all yaml files and organize them in the 3 test suites:
"""Parse all yaml files and organize them in the 4 test suites:
- Automated and Semi-Automated using Chip-Tool
- Simulated using Chip-App1
- Manual
- Automated and Semi-Automated using chip-camera-controller
"""
suites = _init_test_suites(yaml_version)

Expand All @@ -98,7 +104,11 @@ def _parse_all_yaml(
elif test_case.test_type == MatterTestType.SIMULATED:
suites[SuiteType.SIMULATED].add_test_case(test_case)
else:
suites[SuiteType.AUTOMATED].add_test_case(test_case)
if test_case.suite_type == SuiteType.CAMERA_AUTOMATED:
suites[SuiteType.CAMERA_AUTOMATED].add_test_case(test_case)
else:
suites[SuiteType.AUTOMATED].add_test_case(test_case)

except YamlParserException:
# If an exception was raised during parse process, the yaml file will be
# ignored and the loop will continue with the next yaml file
Expand Down
Loading