blob: a8deea4e2c9231f00b8035b66ffa7a51ab860be0 [file] [log] [blame]
#
# 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.
import logging
from typing import List
from mobly import asserts
from matter.clusters import ClusterObjects, Globals, MeterIdentification
from matter.clusters.Types import NullValue
from matter.testing import matter_asserts
from matter.testing.matter_testing import AttributeMatcher, AttributeValue, MatterBaseTest
logger = logging.getLogger(__name__)
cluster = MeterIdentification
class MeterIdentificationTestBaseHelper(MatterBaseTest):
"""This class contains supporting methods for the MeterIdentification test cases."""
# TestEventTriggers IDs
test_event_fake_data = 0x0B06000000000000
test_event_clear = 0x0B06000000000001
async def send_test_event_trigger_attributes_value_set(self):
await self.send_test_event_triggers(eventTrigger=self.test_event_fake_data)
async def send_test_event_clear(self):
await self.send_test_event_triggers(eventTrigger=self.test_event_clear)
async def checkPowerThresholdStruct(self, struct: Globals.Structs.PowerThresholdStruct = None):
"""PowerThresholdStruct type validator."""
if struct.powerThreshold is not None:
matter_asserts.assert_valid_int64(struct.powerThreshold, "PowerThreshold")
if struct.apparentPowerThreshold is not None:
matter_asserts.assert_valid_int64(struct.apparentPowerThreshold, "ApparentPowerThreshold")
if struct.powerThresholdSource is not NullValue:
matter_asserts.assert_valid_enum(
struct.powerThresholdSource,
"PowerThresholdSource attribute must return a Globals.Enums.PowerThresholdSourceEnum",
Globals.Enums.PowerThresholdSourceEnum,
)
async def check_meter_type_attribute(self, endpoint, attribute_value=None):
if not attribute_value:
attribute_value = await self.read_single_attribute_check_success(
endpoint=endpoint, cluster=cluster, attribute=cluster.Attributes.MeterType
)
if attribute_value is not NullValue:
matter_asserts.assert_valid_enum(
attribute_value,
"MeterType attribute must return a Clusters.MeterIdentification.Enums.MeterTypeEnum",
MeterIdentification.Enums.MeterTypeEnum,
)
matter_asserts.assert_int_in_range(attribute_value, 0, 2, "MeterType must be in range 0 - 2")
async def check_point_of_delivery_attribute(self, endpoint, attribute_value=None):
if not attribute_value:
attribute_value = await self.read_single_attribute_check_success(
endpoint=endpoint, cluster=cluster, attribute=cluster.Attributes.PointOfDelivery
)
if attribute_value is not NullValue:
matter_asserts.assert_is_string(attribute_value, "PointOfDelivery must be a string")
asserts.assert_less_equal(len(attribute_value), 64, "PointOfDelivery must have length at most 64!")
async def check_meter_serial_number_attribute(self, endpoint, attribute_value=None):
if not attribute_value:
attribute_value = await self.read_single_attribute_check_success(
endpoint=endpoint, cluster=cluster, attribute=cluster.Attributes.MeterSerialNumber
)
if attribute_value is not NullValue:
matter_asserts.assert_is_string(attribute_value, "MeterSerialNumber must be a string")
asserts.assert_less_equal(len(attribute_value), 64, "MeterSerialNumber must have length at most 64!")
async def check_protocol_version_attribute(self, endpoint, attribute_value=None):
if not attribute_value:
if await self.attribute_guard(endpoint=endpoint, attribute=cluster.Attributes.ProtocolVersion):
attribute_value = await self.read_single_attribute_check_success(
endpoint=endpoint, cluster=cluster, attribute=cluster.Attributes.ProtocolVersion
)
if attribute_value is not NullValue and attribute_value is not None:
matter_asserts.assert_is_string(attribute_value, "ProtocolVersion must be a string")
asserts.assert_less_equal(len(attribute_value), 64, "ProtocolVersion must have length at most 64!")
async def check_power_threshold_attribute(self, endpoint, attribute_value=None):
if not attribute_value:
if await self.feature_guard(endpoint=endpoint, cluster=cluster, feature_int=cluster.Bitmaps.Feature.kPowerThreshold):
attribute_value = await self.read_single_attribute_check_success(
endpoint=endpoint, cluster=cluster, attribute=cluster.Attributes.PowerThreshold
)
if attribute_value is not NullValue:
asserts.assert_true(
isinstance(attribute_value, Globals.Structs.PowerThresholdStruct),
"PowerThreshold must be of type Globals.Structs.PowerThresholdStruct",
)
await self.checkPowerThresholdStruct(struct=attribute_value)
async def verify_reporting(self, reports: dict, attribute: ClusterObjects.ClusterAttributeDescriptor, attribute_name: str, saved_value) -> None:
try:
asserts.assert_not_equal(reports[attribute][0].value, saved_value,
f"""Reported '{attribute_name}' value should be different from saved value.
Subscriptions should only report when values have changed.""")
except KeyError as err:
asserts.fail(f"There are no reports for attribute {attribute_name}:\n{err}")
@staticmethod
def _meter_type_matcher() -> AttributeMatcher:
def predicate(report: AttributeValue) -> bool:
if report.attribute == cluster.Attributes.MeterType:
return True
else:
return False
return AttributeMatcher.from_callable(description="MeterType", matcher=predicate)
@staticmethod
def _point_of_delivery_matcher() -> AttributeMatcher:
def predicate(report: AttributeValue) -> bool:
if report.attribute == cluster.Attributes.PointOfDelivery:
return True
else:
return False
return AttributeMatcher.from_callable(description="PointOfDelivery", matcher=predicate)
@staticmethod
def _meter_serial_number_matcher() -> AttributeMatcher:
def predicate(report: AttributeValue) -> bool:
if report.attribute == cluster.Attributes.MeterSerialNumber:
return True
else:
return False
return AttributeMatcher.from_callable(description="MeterSerialNumber", matcher=predicate)
@staticmethod
def _protocol_version_matcher() -> AttributeMatcher:
def predicate(report: AttributeValue) -> bool:
if report.attribute == cluster.Attributes.ProtocolVersion:
return True
else:
return False
return AttributeMatcher.from_callable(description="ProtocolVersion", matcher=predicate)
@staticmethod
def _power_threshold_matcher() -> AttributeMatcher:
def predicate(report: AttributeValue) -> bool:
if report.attribute == cluster.Attributes.PowerThreshold:
return True
else:
return False
return AttributeMatcher.from_callable(description="PowerThreshold", matcher=predicate)
def get_mandatory_matchers(self) -> List[AttributeMatcher]:
return [
self._meter_type_matcher(),
self._point_of_delivery_matcher(),
self._meter_serial_number_matcher()
]