Skip to content
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

Proposal for TC_MOD_1.2 #37476

Open
wants to merge 13 commits into
base: master
Choose a base branch
from
Open
176 changes: 176 additions & 0 deletions src/python_testing/TC_MOD_1_2.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
#
# Copyright (c) 2025 Project CHIP Authors
# All rights reserved.
#
# 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.
#

# See https://github.com/project-chip/connectedhomeip/blob/master/docs/testing/python.md"#defining-the-ci-test-arguments
# for details about the block below.
#
# === BEGIN CI TEST ARGUMENTS ===
# test-runner-runs:
# run1:
# app: ${ALL_CLUSTERS_APP}
# app-args: --discriminator 1234 --KVS kvs1 --trace-to json:TRACE_APP.json
# script-args: >
# --storage-path admin_storage.json
# --commissioning-method on-network
# --discriminator 1234
# --passcode 20202021
# --PICS src/app/tests/suites/certification/ci-pics-values
# --trace-to json:TRACE_TEST_JSON.json
# --trace-to perfetto:TRACE_TEST_PERFETTO.perfetto
# factory-reset: true
# quiet: true
# === END CI TEST ARGUMENTS ===""import typing

import logging

import chip.clusters as Clusters
from chip.clusters.Types import NullValue
from chip.testing.matter_testing import MatterBaseTest, TestStep, async_test_body, default_matter_test_main
from mobly import asserts

logger = logging.getLogger(__name__)


class MOD_1_2(MatterBaseTest):
"""Proposal test for Mode Select Cluster attributes as a server."""

def desc_MOD_1_2(self) -> str:
return "80.2.1. [TC-MOD-1.2] Cluster attributes with DUT as Server"

def pics_MOD_1_2(self):
"""Return PICS definitions asscociated with this test."""
pics = [
"MOD.S"
]
return pics

def steps_MOD_1_2(self) -> list[TestStep]:
steps = [
TestStep(1, "Commission DUT to TH (can be skipped if done in a preceding test).", is_commissioning=True),
TestStep(2, "TH reads the SupportedModes attribute from DUT"),
TestStep(3, "TH reads the CurrentMode attribute from the DUT"),
TestStep(4, "TH reads the OnMode attribute from the DUT"),
TestStep(5, "TH reads the StartUpMode attribute from the DUT"),
TestStep(6, "TH reads the Description attribute from the DUT"),
TestStep(7, "TH reads the StandardNamespace attribute from the DUT")
]
return steps

def _verify_supported_mode(self, supported_mode):
"""Verify supported mode.
Each mode should be a struct containing a label (user understandable string describing the mode), mode number (integer that identifies the mode and is unique within this SupportedMode list) and list of semantic tags
MfgCode and Value fields present in the SemanticTags structs should be no larger than 16 bits on the TH (Chip-tool)."""
if not hasattr(supported_mode, 'label') and not isinstance(supported_mode.label, str):
asserts.fail("Supported mode struct does not have attribute label or does not contain string ")

if not hasattr(supported_mode, 'mode') and not isinstance(supported_mode.mode, int):
asserts.fail("Supported modes struct does not have attribute mode or does not contain int")

if not hasattr(supported_mode, 'semanticTags'):
asserts.fail("Supported mode struct does not have attribute semanticTags")

# veirfy if we have entries for semanticTags if there are any verfy values no larger that 16 bits
# entries must have the value and manufacturer code.
self._log_attribute("Semantic tags", supported_mode.semanticTags)

if isinstance(supported_mode.semanticTags, list) and len(supported_mode.semanticTags) > 0:
logger.info(
"SupportedMode.semanticTags contains values, verifying attributes for manufacturedcode and value are not longer than 16bits int")
for semantictag in supported_mode.semanticTags:
asserts.assert_true(semantictag.mfgCode >= 0 and semantictag.mfgCode <= 0xFFFF,
"Element semantictag.Mfgcode is greater than 16 bits")
asserts.assert_true(semantictag.value >= 0 and semantictag.value <= 0xFFFF,
"Element semantictag.Value is greater than 16 bits")

def _log_attribute(self, name, value):
logger.info(f"{name} attribute with value: {value} with type: {type(value)}")

@async_test_body
async def test_MOD_1_2(self):
self.cluster = Clusters.ModeSelect
self.endpoint = self.get_endpoint(1)

# Commision device
# In the test plan step 1 is defined as a precondition.
self.step(1)

# Veirfy the Supported modes
# Verify contains attributes label and mode
# Verify if semantic tags has elements in list , and if there are values assert the values
self.step(2)
supported_modes = await self.read_single_attribute_check_success(endpoint=self.endpoint, cluster=self.cluster, attribute=self.cluster.Attributes.SupportedModes)
logger.info(f"Supported modes {supported_modes}")
# List must not be empty
asserts.assert_true(len(supported_modes) > 0, "Supported modes can not be empty.")
supported_modes_values = []
for supported_mode in supported_modes:
logger.info(
f"Label {supported_mode} with type {type(supported_mode)} and {supported_mode.label} and {supported_mode.mode} and {supported_mode.semanticTags}")
# Verify the struct values
self._verify_supported_mode(supported_mode=supported_mode)
# After verifying the struct is correct append the mode value.
supported_modes_values.append(supported_mode.mode)
# Verify mode numbers are unique
asserts.assert_equal(len(supported_modes_values), len(set(supported_modes_values)),
f"Duplicate value found for supported mode values :{supported_modes_values}.")
logger.info(f"Supported modes values {supported_modes_values}")

# Currentmode attribute check must be int and must be in the supported modes values.
self.step(3)
current_mode = await self.read_single_attribute_check_success(endpoint=self.endpoint, cluster=self.cluster, attribute=self.cluster.Attributes.CurrentMode)
self._log_attribute('CurrentMode', current_mode)
asserts.assert_true(isinstance(current_mode, int), "Current mode is not int")
asserts.assert_in(current_mode, supported_modes_values, f"Current mode {current_mode} is not in {supported_modes_values}")

self.step(4)
# DEPONOFF in the Mandatory/Optional Column
if await self.attribute_guard(endpoint=self.endpoint, attribute=self.cluster.Attributes.OnMode):
on_mode = await self.read_single_attribute_check_success(endpoint=self.endpoint, cluster=self.cluster, attribute=self.cluster.Attributes.OnMode)
# On mode can be Nullvalue
self._log_attribute("OnMode", on_mode)
asserts.assert_true((isinstance(on_mode, int) or on_mode is NullValue),
"Onmode is not int or is not Nullvalue")
# Verify that OnMode is in the list of Supported Modes, but if null, cant be verified.
if on_mode is not NullValue:
asserts.assert_in(on_mode, supported_modes_values, f"Onmode {on_mode} is not in {supported_modes_values}")

# Validate startup mode (attribute Startup is optional)
self.step(5)
if await self.attribute_guard(endpoint=self.endpoint, attribute=self.cluster.Attributes.StartUpMode):
startup_mode = await self.read_single_attribute_check_success(endpoint=self.endpoint, cluster=self.cluster, attribute=self.cluster.Attributes.StartUpMode)
self._log_attribute("StartupMode", startup_mode)
asserts.assert_true(isinstance(startup_mode, int), "Startupmode is not int")
asserts.assert_in(startup_mode, supported_modes_values,
f"Startupmode {startup_mode} is not in {supported_modes_values}")

# Verify the string is str and larger that 1 char.
self.step(6)
description = await self.read_single_attribute_check_success(endpoint=self.endpoint, cluster=self.cluster, attribute=self.cluster.Attributes.Description)
self._log_attribute("Description", description)
asserts.assert_true(isinstance(description, str), "Description attribute is not str")
asserts.assert_true(len(description) >= 1, "Description is lower that 1 char.")

# Verify the StandardNamespace can be 16 bits enum or null
self.step(7)
standard_namepace = await self.read_single_attribute_check_success(endpoint=self.endpoint, cluster=self.cluster, attribute=self.cluster.Attributes.StandardNamespace)
self._log_attribute("StandardNamespace", standard_namepace)
asserts.assert_true((standard_namepace is NullValue or (isinstance(standard_namepace, int) and (standard_namepace >= 0 and standard_namepace <= 0xFFFF))),
"Standard namespace is not 16bit enum or not Nullvalue")


if __name__ == "__main__":
default_matter_test_main()
Loading