diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index 0c0290ff193230..58bdd2314c823a 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -560,6 +560,7 @@ jobs: scripts/run_in_python_env.sh out/venv './scripts/tests/run_python_test.py --app out/linux-x64-rvc-ipv6only-no-ble-no-wifi-tsan-clang-test/chip-rvc-app --factoryreset --app-args "--discriminator 1234 --KVS kvs1 --trace_file json:out/trace_data/app-{SCRIPT_BASE_NAME}.json" --script "src/python_testing/TC_RVCOPSTATE_2_3.py" --script-args "--storage-path admin_storage.json --commissioning-method on-network --discriminator 1234 --passcode 20202021 --PICS examples/rvc-app/rvc-common/pics/rvc-app-pics-values --endpoint 1 --trace-to json:out/trace_data/test-{SCRIPT_BASE_NAME}.json --trace-to perfetto:out/trace_data/test-{SCRIPT_BASE_NAME}.perfetto"' scripts/run_in_python_env.sh out/venv './scripts/tests/run_python_test.py --app out/linux-x64-rvc-ipv6only-no-ble-no-wifi-tsan-clang-test/chip-rvc-app --factoryreset --app-args "--discriminator 1234 --KVS kvs1 --trace_file json:out/trace_data/app-{SCRIPT_BASE_NAME}.json" --script "src/python_testing/TC_RVCOPSTATE_2_4.py" --script-args "--storage-path admin_storage.json --commissioning-method on-network --discriminator 1234 --passcode 20202021 --PICS examples/rvc-app/rvc-common/pics/rvc-app-pics-values --endpoint 1 --trace-to json:out/trace_data/test-{SCRIPT_BASE_NAME}.json --trace-to perfetto:out/trace_data/test-{SCRIPT_BASE_NAME}.perfetto"' scripts/run_in_python_env.sh out/venv './src/python_testing/test_testing/test_TC_DA_1_2.py' + scripts/run_in_python_env.sh out/venv './src/python_testing/test_testing/test_TC_ICDM_2_1.py' - name: Uploading core files uses: actions/upload-artifact@v4 if: ${{ failure() && !env.ACT }} diff --git a/src/python_testing/test_testing/MockTestRunner.py b/src/python_testing/test_testing/MockTestRunner.py index 451e38d4660ac6..5d6592b5a8cd41 100644 --- a/src/python_testing/test_testing/MockTestRunner.py +++ b/src/python_testing/test_testing/MockTestRunner.py @@ -37,9 +37,9 @@ async def __call__(self, *args, **kwargs): class MockTestRunner(): - def __init__(self, filename: str, classname: str, test: str, endpoint: int): + def __init__(self, filename: str, classname: str, test: str, endpoint: int, pics: dict[str, bool] = {}): self.config = MatterTestConfig( - tests=[test], endpoint=endpoint, dut_node_ids=[1]) + tests=[test], endpoint=endpoint, dut_node_ids=[1], pics=pics) self.stack = MatterStackState(self.config) self.default_controller = self.stack.certificate_authorities[0].adminList[0].NewController( nodeId=self.config.controller_node_id, diff --git a/src/python_testing/test_testing/test_TC_ICDM_2_1.py b/src/python_testing/test_testing/test_TC_ICDM_2_1.py new file mode 100755 index 00000000000000..4f8371b39a3a5f --- /dev/null +++ b/src/python_testing/test_testing/test_TC_ICDM_2_1.py @@ -0,0 +1,237 @@ +#!/usr/bin/env -S python3 -B +# +# Copyright (c) 2024 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 string +import sys +import typing +from dataclasses import dataclass + +import chip.clusters as Clusters +from chip.clusters import Attribute +from chip.clusters.Types import NullValue +from MockTestRunner import MockTestRunner + +c = Clusters.IcdManagement +attr = c.Attributes +uat = c.Bitmaps.UserActiveModeTriggerBitmap + + +@dataclass +class ICDMData(): + FeatureMap: int + IdleModeDuration: int + ActiveModeDuration: int + ActiveModeThreshold: int + RegisteredClients: list + ICDCounter: int + ClientsSupportedPerFabric: int + UserActiveModeTriggerHint: int + UserActiveModeTriggerInstruction: string + OperatingMode: c.Enums.OperatingModeEnum + expect_pass: bool + + +long_string = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut e" +too_long_string = long_string + "1" + +TEST_CASES = [ + + # ============================== + # ICDM 2.1 Test cases + # ============================== + # -------- + # Test cases to validate IdleModeDuration + # -------- + # IdleModeDuration under minimum (< 1) + ICDMData(0, 0, 0, 100, [], 0, 2, 0, "", + c.Enums.OperatingModeEnum.kSit, False), + # IdleModeDuration at minimum + ICDMData(0, 1, 0, 100, [], 0, 2, 0, "", + c.Enums.OperatingModeEnum.kSit, True), + # IdleModeDuration at maximum + ICDMData(0, 64800, 100, 100, [], 0, 2, 0, "", + c.Enums.OperatingModeEnum.kSit, True), + # IdleModeDuration over maximum (>64800) + ICDMData(0, 64801, 100, 100, [], 0, 2, 0, "", + c.Enums.OperatingModeEnum.kSit, False), + # IdleModeDuration < ActiveModeDuration + ICDMData(0, 1, 1001, 100, [], 0, 2, 0, "", + c.Enums.OperatingModeEnum.kSit, False), + # -------- + # Test cases to validate ActiveModeDuration + # -------- + # ActiveModeDuration under minimum + ICDMData(0, 100, -1, 100, [], 0, 2, 0, "", + c.Enums.OperatingModeEnum.kSit, False), + # ActiveModeDuration at minimum + ICDMData(0, 100, 0, 100, [], 0, 2, 0, "", + c.Enums.OperatingModeEnum.kSit, True), + # ActiveModeDuration at maximum - value is max IdleModeDuration value - 1 + ICDMData(0, 64800, 0x3DCC4FF, 100, [], 0, 2, 0, "", + c.Enums.OperatingModeEnum.kSit, True), + # -------- + # Test cases to validate ActiveModeThreshold + # -------- + # ActiveModeThreshold < minimum + ICDMData(0, 1, 0, -1, [], 0, 2, 0, "", + c.Enums.OperatingModeEnum.kSit, False), + # ActiveModeThreshold at SIT minimum + ICDMData(0, 1, 0, 0, [], 0, 2, 0, "", + c.Enums.OperatingModeEnum.kSit, True), + # ActiveModeThreshold under LIT minimum + ICDMData(0x7, 1, 0, 4999, [], 0, 2, 0, "", + c.Enums.OperatingModeEnum.kLit, False), + # ActiveModeThreshold at LIT minimum + ICDMData(0x7, 1, 0, 5000, [], 0, 2, 0, "", + c.Enums.OperatingModeEnum.kLit, True), + # ActiveModeThreshold at Maximum + ICDMData(0, 1, 0, 0xFFFF, [], 0, 2, 0, "", + c.Enums.OperatingModeEnum.kSit, True), + # ActiveModeThreshold over Maximum + ICDMData(0, 1, 0, 0x10000, [], 0, 2, 0, "", + c.Enums.OperatingModeEnum.kSit, False), + # -------- + # Test cases to validate ClientsSupportedPerFabric + # -------- + # ClientsSupportedPerFabric under minimum (< 1) + ICDMData(0, 1, 0, 100, [], 0, 0, 0, "", + c.Enums.OperatingModeEnum.kLit, False), + # ClientsSupportedPerFabric at minimum + ICDMData(0, 1, 0, 100, [], 0, 1, 0, "", + c.Enums.OperatingModeEnum.kLit, True), + # ClientsSupportedPerFabric at maximum + ICDMData(0, 1, 0, 100, [], 0, 255, 0, "", + c.Enums.OperatingModeEnum.kLit, True), + # ClientsSupportedPerFabric > maximum + ICDMData(0, 1, 0, 100, [], 0, 256, 0, "", + c.Enums.OperatingModeEnum.kLit, True), + # -------- + # Test cases to validate RegisteredClients + # -------- + # Incorrect type + ICDMData(0, 1, 0, 100, 0, 0, 1, 0, "", + c.Enums.OperatingModeEnum.kLit, False), + # Correct type + ICDMData(0, 1, 0, 100, [], 0, 1, 0, "", + c.Enums.OperatingModeEnum.kLit, True), + # -------- + # Test cases to validate ICDCounter + # -------- + # ICDCounter under minimum (< 0) + ICDMData(0, 1, 0, 100, [], -1, 1, 0, "", + c.Enums.OperatingModeEnum.kLit, False), + # ICDCounter at minimum + ICDMData(0, 1, 0, 100, [], 0, 1, 0, "", + c.Enums.OperatingModeEnum.kLit, True), + # ICDCounter at maximum + ICDMData(0, 1, 0, 100, [], 0xFFFFFFFF, 1, 0, "", + c.Enums.OperatingModeEnum.kLit, True), + # ICDCounter over maximum + ICDMData(0, 1, 0, 100, [], 0x100000000, 1, 0, "", + c.Enums.OperatingModeEnum.kLit, False), + # -------- + # Test cases to validate UserActiveModeTriggerHint + # -------- + # UserActiveModeTriggerHint outsite valid range + ICDMData(0, 1, 0, 100, [], 0, 1, 0x1FFFF, "", + c.Enums.OperatingModeEnum.kLit, False), + # UserActiveModeTriggerHint outsite valid range + ICDMData(0, 1, 0, 100, [], 0, 1, -1, "", + c.Enums.OperatingModeEnum.kLit, False), + # UserActiveModeTriggerHint with no hints + ICDMData(0, 1, 0, 100, [], 0, 1, 0, "", + c.Enums.OperatingModeEnum.kLit, True), + # UserActiveModeTriggerHint wiht two instruction depedent bits set + ICDMData(0, 1, 0, 100, [], 0, 1, uat.kCustomInstruction | uat.kActuateSensorSeconds, "", + c.Enums.OperatingModeEnum.kLit, False), + # -------- + # Test cases to validate UserActiveModeTriggerInstruction + # -------- + # UserActiveModeTriggerInstruction with wrong encoding + ICDMData(0, 1, 0, 100, [], 0, 1, uat.kCustomInstruction, "Hello\uD83D\uDE00World", + c.Enums.OperatingModeEnum.kLit, False), + # UserActiveModeTriggerInstruction with empty string + ICDMData(0, 1, 0, 100, [], 0, 1, uat.kCustomInstruction, "", + c.Enums.OperatingModeEnum.kLit, True), + # UserActiveModeTriggerInstruction with empty string + ICDMData(0, 1, 0, 100, [], 0, 1, uat.kCustomInstruction, "", + c.Enums.OperatingModeEnum.kLit, True), + # UserActiveModeTriggerInstruction with max string length + ICDMData(0, 1, 0, 100, [], 0, 1, uat.kCustomInstruction, long_string, + c.Enums.OperatingModeEnum.kLit, True), + # UserActiveModeTriggerInstruction > max string length + ICDMData(0, 1, 0, 100, [], 0, 1, uat.kCustomInstruction, too_long_string, + c.Enums.OperatingModeEnum.kLit, False), + # UserActiveModeTriggerInstruction invalid number - Trailing 0s + ICDMData(0, 1, 0, 100, [], 0, 1, uat.kActuateSensorSeconds, "001", + c.Enums.OperatingModeEnum.kLit, False), + # UserActiveModeTriggerInstruction invalid number - Letters + ICDMData(0, 1, 0, 100, [], 0, 1, uat.kActuateSensorSeconds, "not a number", + c.Enums.OperatingModeEnum.kLit, False), + # UserActiveModeTriggerInstruction Valid number + ICDMData(0, 1, 0, 100, [], 0, 1, uat.kActuateSensorSeconds, "100000", + c.Enums.OperatingModeEnum.kLit, True), + # -------- + # Test cases to validate OpertingMode + # -------- + # OpertingMode with negative value + ICDMData(0, 1, 0, 100, [], 0, 1, uat.kActuateSensorSeconds, "100000", + -1, False), + # OpertingMode with Accepted value + ICDMData(0, 1, 0, 100, [], 0, 1, uat.kActuateSensorSeconds, "100000", + c.Enums.OperatingModeEnum.kLit, True), + # OpertingMode with unkown value + ICDMData(0, 1, 0, 100, [], 0, 1, uat.kActuateSensorSeconds, "100000", + c.Enums.OperatingModeEnum.kUnknownEnumValue, False), + +] + + +def test_spec_to_attribute_cache(test_icdm: ICDMData) -> Attribute.AsyncReadTransaction.ReadResponse: + resp = Attribute.AsyncReadTransaction.ReadResponse({}, [], {}) + resp.attributes = {0: {c: {attr.FeatureMap: test_icdm.FeatureMap, attr.IdleModeDuration: test_icdm.IdleModeDuration, attr.ActiveModeDuration: test_icdm.ActiveModeDuration, attr.ActiveModeThreshold: test_icdm.ActiveModeThreshold, + attr.RegisteredClients: test_icdm.RegisteredClients, attr.ICDCounter: test_icdm.ICDCounter, + attr.ClientsSupportedPerFabric: test_icdm.ClientsSupportedPerFabric, attr.UserActiveModeTriggerHint: test_icdm.UserActiveModeTriggerHint, + attr.UserActiveModeTriggerInstruction: test_icdm.UserActiveModeTriggerInstruction, attr.OperatingMode: test_icdm.OperatingMode}}} + return resp + + +def main(): + pics = {"ICDM.S.A0000": True, "ICDM.S.A0001": True, "ICDM.S.A0002": True, "ICDM.S.A0003": True, "ICDM.S.A0004": True, + "ICDM.S.A0005": True, "ICDM.S.A0006": True, "ICDM.S.A0007": True, "ICDM.S.A0008": True, } + + test_runner = MockTestRunner( + 'TC_ICDM_2_1', 'TC_ICDM_2_1', 'test_TC_ICDM_2_1', 0, pics) + failures = [] + for idx, t in enumerate(TEST_CASES): + ok = test_runner.run_test_with_mock_read( + test_spec_to_attribute_cache(t)) == t.expect_pass + if not ok: + failures.append(f"Measured test case failure: {idx} {t}") + + test_runner.Shutdown() + print( + f"Test of tests: run {len(TEST_CASES)}, test response correct: {len(TEST_CASES) - len(failures)} | test response incorrect: {len(failures)}") + for f in failures: + print(f) + + return 1 if failures else 0 + + +if __name__ == "__main__": + sys.exit(main())