diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index 2907a4a89711ea..76b20631646ce3 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -526,6 +526,7 @@ jobs: scripts/run_in_python_env.sh out/venv './scripts/tests/run_python_test.py --app out/linux-x64-all-clusters-ipv6only-no-ble-no-wifi-tsan-clang-test/chip-all-clusters-app --factoryreset --app-args "--discriminator 1234 --KVS kvs1 --trace-to json:out/trace_data/app-{SCRIPT_BASE_NAME}.json" --script "src/python_testing/TC_TIMESYNC_3_1.py" --script-args "--storage-path admin_storage.json --commissioning-method on-network --discriminator 1234 --passcode 20202021 --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-all-clusters-ipv6only-no-ble-no-wifi-tsan-clang-test/chip-all-clusters-app --factoryreset --app-args "--discriminator 1234 --KVS kvs1 --trace-to json:out/trace_data/app-{SCRIPT_BASE_NAME}.json --enable-key 000102030405060708090a0b0c0d0e0f" --script "src/python_testing/TC_TestEventTrigger.py" --script-args "--storage-path admin_storage.json --bool-arg allow_sdk_dac:true --commissioning-method on-network --discriminator 1234 --passcode 20202021 --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-all-clusters-ipv6only-no-ble-no-wifi-tsan-clang-test/chip-all-clusters-app --factoryreset --app-args "--discriminator 1234 --KVS kvs1 --trace-to json:out/trace_data/app-{SCRIPT_BASE_NAME}.json" --script "src/python_testing/TestBatchInvoke.py" --script-args "--storage-path admin_storage.json --commissioning-method on-network --discriminator 1234 --passcode 20202021 --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-all-clusters-ipv6only-no-ble-no-wifi-tsan-clang-test/chip-all-clusters-app --factoryreset --app-args "--discriminator 1234 --KVS kvs1 --trace-to json:out/trace_data/app-{SCRIPT_BASE_NAME}.json" --script "src/python_testing/TC_DISHM_1_2.py" --script-args "--storage-path admin_storage.json --commissioning-method on-network --discriminator 1234 --passcode 20202021 --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 --script "src/python_testing/TestConformanceSupport.py" --script-args "--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-all-clusters-ipv6only-no-ble-no-wifi-tsan-clang-test/chip-all-clusters-app --factoryreset --app-args "--discriminator 1234 --KVS kvs1 --trace-to json:out/trace_data/app-{SCRIPT_BASE_NAME}.json" --script "src/python_testing/TestGroupTableReports.py" --script-args "--storage-path admin_storage.json --commissioning-method on-network --discriminator 1234 --passcode 20202021 --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 --script "src/python_testing/TestMatterTestingSupport.py" --script-args "--trace-to json:out/trace_data/test-{SCRIPT_BASE_NAME}.json --trace-to perfetto:out/trace_data/test-{SCRIPT_BASE_NAME}.perfetto"' diff --git a/src/app/tests/suites/certification/Test_TC_DISHM_1_1.yaml b/src/app/tests/suites/certification/Test_TC_DISHM_1_1.yaml index da8a0924274e9e..3ccb92bc8bd2b8 100644 --- a/src/app/tests/suites/certification/Test_TC_DISHM_1_1.yaml +++ b/src/app/tests/suites/certification/Test_TC_DISHM_1_1.yaml @@ -13,94 +13,103 @@ # limitations under the License. # Auto-generated scripts for harness use only, please review before automation. The endpoints and cluster names are currently set to default -name: 263.1.1. [TC-DISHM-1.1] Global attributes with DUT as Server +name: 194.1.1. [TC-DISHM-1.1] Global Attributes with DUT as Server PICS: - DISHM.S config: nodeId: 0x12344321 - cluster: "Basic Information" - endpoint: 0 + cluster: "Dishwasher Mode" + endpoint: 1 tests: - - label: - "Step 1: Commission DUT to TH (can be skipped if done in a preceding - test)." - verification: | - - disabled: true - - - label: "Step 2: TH reads from the DUT the ClusterRevision attribute." - verification: | - ./chip-tool dishwashermode read cluster-revision 1 1 - - Verify the "ClusterRevision" value is of unit16 and reflects the highest revision number 2 on the TH(Chip-tool) and below is the sample log provided for the raspi platform: - - CHIP:DMG : } - CHIP:TOO : Endpoint: 1 Cluster: 0x0000_0059 Attribute 0x0000_FFFD DataVersion: 2488070594 - CHIP:TOO : ClusterRevision: 2 - disabled: true - - - label: "Step 3: TH reads from the DUT the FeatureMap attribute." - verification: | - ./chip-tool dishwashermode read feature-map 1 1 - - On the TH(Chip-tool) Log, Verify featureMap value is 0 and below is the sample log provided for the raspi platform: - - [1690365613.351850][27441:27443] CHIP:TOO: Endpoint: 1 Cluster: 0x0000_0059 Attribute 0x0000_FFFC DataVersion: 1130015440 - [1690365613.351911][27441:27443] CHIP:TOO: FeatureMap: 1 - disabled: true - - - label: "Step 4: TH reads from the DUT the AttributeList attribute." - verification: | - ./chip-tool dishwashermode read attribute-list 1 1 - - Verify the "AttributeList " should include the mandatory attributes (values 0, 1), - - Global attributes (value 65533, 65532, 65531, 65529 and 65528) and - - List may include optional attribute(value 0x0002), if DISHM.S.A0002(StartUpMode) supports, on the TH(Chip-tool) Log and below is the sample log provided for the raspi platform: + - label: "1. Wait for the commissioned device to be retrieved" + cluster: "DelayCommands" + command: "WaitForCommissionee" + arguments: + values: + - name: "nodeId" + value: nodeId + + - label: "2. TH reads the ClusterRevision from DUT" + command: "readAttribute" + attribute: "ClusterRevision" + response: + value: 2 + constraints: + type: int16u + + - label: "3. TH reads the FeatureMap from DUT" + PICS: " !DISHM.S.F00 " + command: "readAttribute" + attribute: "FeatureMap" + response: + value: 0 + constraints: + type: bitmap32 + + - label: "4. TH reads AttributeList from DUT" + PICS: PICS_EVENT_LIST_ENABLED + command: "readAttribute" + attribute: "AttributeList" + response: + constraints: + type: list + contains: [0, 1, 65528, 65529, 65530, 65531, 65532, 65533] + + - label: "4. TH reads AttributeList from DUT" + PICS: "!PICS_EVENT_LIST_ENABLED" + command: "readAttribute" + attribute: "AttributeList" + response: + constraints: + type: list + contains: [0, 1, 65528, 65529, 65531, 65532, 65533] - [1696402605.599359][7921:7923] CHIP:TOO: Endpoint: 1 Cluster: 0x0000_0059 Attribute 0x0000_FFFB DataVersion: 712950283 - [1696402605.599377][7921:7923] CHIP:TOO: AttributeList: 9 entries - [1696402605.599382][7921:7923] CHIP:TOO: [1]: 0 - [1696402605.599385][7921:7923] CHIP:TOO: [2]: 1 - [1696402605.599388][7921:7923] CHIP:TOO: [3]: 2 - [1696402605.599391][7921:7923] CHIP:TOO: [4]: 3 - [1696402605.599393][7921:7923] CHIP:TOO: [5]: 65528 - [1696402605.599396][7921:7923] CHIP:TOO: [6]: 65529 - [1696402605.599399][7921:7923] CHIP:TOO: [7]: 65531 - [1696402605.599402][7921:7923] CHIP:TOO: [8]: 65532 - [1696402605.599404][7921:7923] CHIP:TOO: [9]: 65533 - disabled: true - - - label: "Step 5: TH reads from the DUT the EventList attribute." - verification: | - ./chip-tool dishwashermode read event-list 1 1 - - * Step 5 is currently not supported and SHALL be skipped. - - [1696402636.316151][7926:7928] CHIP:DMG: } - [1696402636.316183][7926:7928] CHIP:TOO: Response Failure: IM Error 0x00000586: General error: 0x86 (UNSUPPORTED_ATTRIBUTE) - disabled: true - - - label: "Step 6: TH reads from the DUT the AcceptedCommandList attribute." - verification: | - ./chip-tool dishwashermode read accepted-command-list 1 1 - - Verify the "AcceptedCommandList" contains a list of mandatory commands (value 0) on the TH (Chip-tool) and below is the sample log provided for the raspi platform: - - [1690365651.143190][27451:27453] CHIP:TOO: Endpoint: 1 Cluster: 0x0000_0059 Attribute 0x0000_FFF9 DataVersion: 1130015440 - [1690365651.143256][27451:27453] CHIP:TOO: AcceptedCommandList: 1 entries - [1690365651.143284][27451:27453] CHIP:TOO: [1]: 0 - disabled: true - - - label: "Step 7: TH reads from the DUT the GeneratedCommandList attribute." - verification: | - ./chip-tool dishwashermode read generated-command-list 1 1 - - Verify " GeneratedCommandList" contains a list of mandatory commands (value 1) on the TH(Chip-tool) Log and below is the sample log provided for the raspi platform: + - label: + "4. TH reads optional attribute (DISHM.S.A0002(StartUpMode)) in + AttributeList" + PICS: DISHM.S.A0002 + command: "readAttribute" + attribute: "AttributeList" + response: + constraints: + type: list + contains: [2] - [1689997224.280302][360025:360027] CHIP:TOO: Endpoint: 1 Cluster: 0x0000_0059 Attribute 0x0000_FFF8 DataVersion: 1427220838 - [1689997224.280330][360025:360027] CHIP:TOO: GeneratedCommandList: 1 entries - [1689997224.280346][360025:360027] CHIP:TOO: [1]: 1 - disabled: true + - label: + "4. TH reads optional attribute (DISHM.S.A0002(StartUpMode)) in + AttributeList" + PICS: " !DISHM.S.A0002 " + command: "readAttribute" + attribute: "AttributeList" + response: + constraints: + type: list + excludes: [2] + + - label: "5. TH reads from the DUT the EventList attribute" + PICS: PICS_EVENT_LIST_ENABLED + command: "readAttribute" + attribute: "EventList" + response: + value: [] + constraints: + type: list + + - label: "6. TH reads AcceptedCommandList from DUT" + command: "readAttribute" + attribute: "AcceptedCommandList" + response: + constraints: + type: list + contains: [0] + + - label: "7. TH reads from the DUT the GeneratedCommandList attribute" + command: "readAttribute" + attribute: "GeneratedCommandList" + response: + constraints: + type: list + contains: [1] diff --git a/src/app/tests/suites/ciTests.json b/src/app/tests/suites/ciTests.json index 26efd799ad7efd..200810a973278a 100644 --- a/src/app/tests/suites/ciTests.json +++ b/src/app/tests/suites/ciTests.json @@ -57,6 +57,7 @@ "Test_TC_DGETH_2_2" ], "DiagnosticsLogs": ["Test_TC_DLOG_1_1"], + "DishwasherMode": ["Test_TC_DISHM_1_1"], "EnergyEVSE": ["Test_TC_EEVSE_1_1", "Test_TC_EEVSE_2_1"], "EnergyEVSEMode": [ "Test_TC_EEVSEM_1_1", @@ -406,6 +407,7 @@ "DeviceEnergyManagementMode", "Descriptor", "DiagnosticsLogs", + "DishwasherMode", "EthernetNetworkDiagnostics", "EnergyEVSE", "EnergyEVSEMode", diff --git a/src/app/tests/suites/manualTests.json b/src/app/tests/suites/manualTests.json index d199a682492150..5d72cfdeaae98b 100644 --- a/src/app/tests/suites/manualTests.json +++ b/src/app/tests/suites/manualTests.json @@ -73,14 +73,6 @@ "Test_TC_DISHALM_3_5", "Test_TC_DISHALM_3_6" ], - "DishwasherMode": [ - "Test_TC_DISHM_1_1", - "Test_TC_DISHM_1_2", - "Test_TC_DISHM_2_1", - "Test_TC_DISHM_3_1", - "Test_TC_DISHM_3_2", - "Test_TC_DISHM_3_3" - ], "DeviceManagement": [ "Test_TC_BINFO_3_1", "Test_TC_OPCREDS_3_1", @@ -321,7 +313,6 @@ "DeviceManagement", "DiagnosticsLogs", "DishwasherAlarm", - "DishwasherMode", "Descriptor", "DeviceEnergyManagementMode", "FanControl", diff --git a/src/python_testing/TC_DISHM_1_2.py b/src/python_testing/TC_DISHM_1_2.py new file mode 100644 index 00000000000000..e3e3d34fa839c4 --- /dev/null +++ b/src/python_testing/TC_DISHM_1_2.py @@ -0,0 +1,130 @@ +# +# Copyright (c) 2023 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 + +import chip.clusters as Clusters +from chip.clusters.Types import NullValue +from matter_testing_support import MatterBaseTest, TestStep, async_test_body, default_matter_test_main +from mobly import asserts + +class TC_DISHM_1_2(MatterBaseTest): + + async def read_mode_attribute_expect_success(self, endpoint, attribute): + cluster = Clusters.Objects.DishwasherMode + return await self.read_single_attribute_check_success(endpoint=endpoint, cluster=cluster, attribute=attribute) + + def desc_TC_DISHM_1_2(self) -> str: + return "[TC-DISHM-1.2] Cluster attributes with DUT as Server" + + def steps_TC_DISHM_1_2(self) -> list[TestStep]: + steps = [ + TestStep(1, "Commissioning, already done", is_commissioning=True), + TestStep(2, "TH reads from the DUT the SupportedModes attribute."), + TestStep(3, "TH reads from the DUT the CurrentMode attribute."), + ] + return steps + + def pics_TC_DISHM_1_2(self) -> list[str]: + pics = [ + "DISHM.S", + ] + return pics + + + @async_test_body + async def test_TC_DISHM_1_2(self): + + self.endpoint = self.user_params.get("endpoint", 1) + logging.info("This test expects to find this cluster on endpoint 1") + + attributes = Clusters.DishwasherMode.Attributes + + self.step(1) + + self.step(2) + supported_modes = await self.read_mode_attribute_expect_success(endpoint=self.endpoint, attribute=attributes.SupportedModes) + + logging.info("SupportedModes: %s" % (supported_modes)) + + # Check the number of modes in SupportedModes. + asserts.assert_greater_equal(len(supported_modes), 2, "SupportedModes must have at least two entries!") + asserts.assert_less_equal(len(supported_modes), 255, "SupportedModes must have at most 255 entries!") + + # Check that each Mode field is unique + supported_modes_dut = [] + for m in supported_modes: + if m.mode in supported_modes_dut: + asserts.fail("SupportedModes must have unique mode values!") + else: + supported_modes_dut.append(m.mode) + + # Check that each label is unique + labels = [] + for m in supported_modes: + if m.label in labels: + asserts.fail("SupportedModes must have unique mode label values!") + else: + labels.append(m.label) + + # common mode tags + commonTags = {0x0: 'Auto', + 0x1: 'Quick', + 0x2: 'Quiet', + 0x3: 'LowNoise', + 0x4: 'LowEnergy', + 0x5: 'Vacation', + 0x6: 'Min', + 0x7: 'Max', + 0x8: 'Night', + 0x9: 'Day'} + + # kUnknownEnumValue may not be defined + try: + modeTags = [tag.value for tag in Clusters.DishwasherMode.Enums.ModeTag + if tag is not Clusters.DishwasherMode.Enums.ModeTag.kUnknownEnumValue] + except AttributeError: + modeTags = Clusters.DishwasherMode.Enums.ModeTag + + normal_present = false + for m in supported_modes: + # need at least 1 mode tag entry + asserts.assert_greater(len(m.modeTags, 0, "Must have at least one mode tag.")) + for t in m.modeTags: + # value can't exceed 16 bits + asserts.assert_true(t.value <= 0xFFFF, "Tag value is > 16 bits") + + # check that value field is as expected + is_mfg = (0x8000 <= t.value and t.value <= 0xBFFF) + asserts.assert_true(t.value in commonTags.keys() or t.value in modeTags or is_mfg, + "Found a SupportedModes entry with invalid mode tag value!") + + # is this a normal tag? (need at least 1) + if t.value == Clusters.DishwasherMode.Enums.ModeTag.kNormal: + normal_present = True + + # need at least one mode with the normal tag + asserts.assert_true(normal_present, "The Supported Modes does not have an entry of Normal(0x4000)") + + self.step(3) + current_mode = await self.read_mode_attribute_expect_success(endpoint=self.endpoint, attribute=attributes.CurrentMode) + + logging.info("CurrentMode: %s" % (current_mode)) + asserts.assert_true(current_mode in supported_modes_dut, "CurrentMode is not a supported mode!") + +if __name__ == "__main__": + default_matter_test_main() diff --git a/src/python_testing/TC_DISHM_2_1.py b/src/python_testing/TC_DISHM_2_1.py new file mode 100644 index 00000000000000..834c902d96f285 --- /dev/null +++ b/src/python_testing/TC_DISHM_2_1.py @@ -0,0 +1,159 @@ +# +# Copyright (c) 2023 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 + +import chip.clusters as Clusters +from matter_testing_support import MatterBaseTest, async_test_body, default_matter_test_main, type_matches +from mobly import asserts + + +class TC_DISHM_2_1(MatterBaseTest): + + async def read_mode_attribute_expect_success(self, endpoint, attribute): + cluster = Clusters.Objects.DishwasherMode + return await self.read_single_attribute_check_success(endpoint=endpoint, cluster=cluster, attribute=attribute) + + async def send_change_to_mode_cmd(self, newMode) -> Clusters.Objects.DishwasherMode.Commands.ChangeToModeResponse: + ret = await self.send_single_cmd(cmd=Clusters.Objects.DishwasherMode.Commands.ChangeToMode(newMode=newMode), endpoint=self.endpoint) + asserts.assert_true(type_matches(ret, Clusters.Objects.DishwasherMode.Commands.ChangeToModeResponse), + "Unexpected return type for ChangeToMode") + return ret + + @async_test_body + async def test_TC_DISHM_2_1(self): + + asserts.assert_true('PIXIT.DISHM.MODE_CHANGE_OK' in self.matter_test_config.global_test_params, + "PIXIT.DISHM.MODE_CHANGE_OK must be included on the command line in " + "the --int-arg flag as PIXIT.DISHM.MODE_CHANGE_OK:") + asserts.assert_true('PIXIT.DISHM.MODE_CHANGE_FAIL' in self.matter_test_config.global_test_params, + "PIXIT.DISHM.MODE_CHANGE_FAIL must be included on the command line in " + "the --int-arg flag as PIXIT.DISHM.MODE_CHANGE_FAIL:") + + self.endpoint = self.user_params.get("endpoint", 1) + logging.info("This test expects to find this cluster on endpoint 1") + + self.modeOk = self.matter_test_config.global_test_params['PIXIT.DISHM.MODE_CHANGE_OK'] + self.modeFail = self.matter_test_config.global_test_params['PIXIT.DISHM.MODE_CHANGE_FAIL'] + + asserts.assert_true(self.check_pics("DISHM.S.A0000"), "DISHM.S.A0000 must be supported") + asserts.assert_true(self.check_pics("DISHM.S.A0001"), "DISHM.S.A0001 must be supported") + asserts.assert_true(self.check_pics("DISHM.S.C00.Rsp"), "DISHM.S.C00.Rsp must be supported") + asserts.assert_true(self.check_pics("DISHM.S.C01.Tx"), "DISHM.S.C01.Tx must be supported") + + attributes = Clusters.DishwasherMode.Attributes + + self.print_step(1, "Commissioning, already done") + + self.print_step(2, "Read SupportedModes attribute") + supported_modes = await self.read_mode_attribute_expect_success(endpoint=self.endpoint, attribute=attributes.SupportedModes) + + logging.info("SupportedModes: %s" % (supported_modes)) + + asserts.assert_greater_equal(len(supported_modes), 2, "SupportedModes must have at least two entries!") + + supported_modes_dut = [m.mode for m in supported_modes] + + self.print_step(3, "Read CurrentMode attribute") + + old_current_mode_dut = await self.read_mode_attribute_expect_success(endpoint=self.endpoint, attribute=attributes.CurrentMode) + + logging.info("CurrentMode: %s" % (old_current_mode_dut)) + + # pick a value that's not in the list of supported modes + invalid_mode_th = max(supported_modes_dut) + 1 + + from enum import Enum + + class CommonCodes(Enum): + SUCCESS = 0x00 + UNSUPPORTED_MODE = 0x01 + GENERIC_FAILURE = 0x02 + INVALID_IN_MODE = 0x03 + + self.print_step(4, "Send ChangeToMode command with NewMode set to %d" % (old_current_mode_dut)) + + ret = await self.send_change_to_mode_cmd(newMode=old_current_mode_dut) + asserts.assert_true(ret.status == CommonCodes.SUCCESS.value, "Changing the mode to the current mode should be a no-op") + + if self.check_pics("DISHM.S.M.CAN_TEST_MODE_FAILURE"): + self.print_step(5, "Manually put the device in a state from which it will FAIL to transition to mode %d" % (self.modeFail)) + input("Press Enter when done.\n") + + self.print_step(6, "Read CurrentMode attribute") + old_current_mode_dut = await self.read_mode_attribute_expect_success(endpoint=self.endpoint, attribute=attributes.CurrentMode) + + logging.info("CurrentMode: %s" % (old_current_mode_dut)) + + self.print_step(7, "Send ChangeToMode command with NewMode set to %d" % (self.modeFail)) + + ret = await self.send_change_to_mode_cmd(newMode=self.modeFail) + st = ret.status + logging.info("ChangeToMode Status: %s" % (ret.status)) + is_mfg_code = st in range(0x80, 0xC0) + is_err_code = (st == CommonCodes.GENERIC_FAILURE.value) or (st == CommonCodes.INVALID_IN_MODE.value) or is_mfg_code + asserts.assert_true( + is_err_code, "Changing to mode %d must fail due to the current state of the device" % (self.modeFail)) + logging.info("Status Text: %s" % (ret.statusText)) + st_text_len = len(ret.statusText) + asserts.assert_true(st_text_len in range(1, 65), "StatusText length (%d) must be between 1 and 64" % (st_text_len)) + + self.print_step(8, "Read CurrentMode attribute") + current_mode = await self.read_mode_attribute_expect_success(endpoint=self.endpoint, attribute=attributes.CurrentMode) + + logging.info("CurrentMode: %s" % (current_mode)) + + asserts.assert_true(current_mode == old_current_mode_dut, "CurrentMode changed after failed ChangeToMode command!") + + self.print_step(9, "Manually put the device in a state from which it will SUCCESSFULLY transition to mode %d" % (self.modeOk)) + input("Press Enter when done.\n") + + self.print_step(10, "Read CurrentMode attribute") + old_current_mode_dut = await self.read_mode_attribute_expect_success(endpoint=self.endpoint, attribute=attributes.CurrentMode) + + logging.info("CurrentMode: %s" % (old_current_mode_dut)) + + self.print_step(11, "Send ChangeToMode command with NewMode set to %d" % (self.modeOk)) + + ret = await self.send_change_to_mode_cmd(newMode=self.modeOk) + asserts.assert_true(ret.status == CommonCodes.SUCCESS.value, + "Changing to mode %d must succeed due to the current state of the device" % (self.modeOk)) + + self.print_step(12, "Read CurrentMode attribute") + current_mode = await self.read_mode_attribute_expect_success(endpoint=self.endpoint, attribute=attributes.CurrentMode) + + logging.info("CurrentMode: %s" % (current_mode)) + + asserts.assert_true(current_mode == self.modeOk, + "CurrentMode doesn't match the argument of the successful ChangeToMode command!") + + self.print_step(13, "Send ChangeToMode command with NewMode set to %d" % (invalid_mode_th)) + + ret = await self.send_change_to_mode_cmd(newMode=invalid_mode_th) + asserts.assert_true(ret.status == CommonCodes.UNSUPPORTED_MODE.value, + "Attempt to change to invalid mode %d didn't fail as expected" % (invalid_mode_th)) + + self.print_step(14, "Read CurrentMode attribute") + current_mode = await self.read_mode_attribute_expect_success(endpoint=self.endpoint, attribute=attributes.CurrentMode) + + logging.info("CurrentMode: %s" % (current_mode)) + + asserts.assert_true(current_mode == self.modeOk, "CurrentMode changed after failed ChangeToMode command!") + + +if __name__ == "__main__": + default_matter_test_main() diff --git a/src/python_testing/TC_DISHM_3_1.py b/src/python_testing/TC_DISHM_3_1.py new file mode 100644 index 00000000000000..880e81c80e531d --- /dev/null +++ b/src/python_testing/TC_DISHM_3_1.py @@ -0,0 +1,170 @@ +# +# Copyright (c) 2023 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 + +import chip.clusters as Clusters +import chip.tlv +from chip.clusters.Types import NullValue +from chip.interaction_model import Status +from matter_testing_support import MatterBaseTest, async_test_body, default_matter_test_main, type_matches +from mobly import asserts + + +class TC_DISHM_3_1(MatterBaseTest): + + async def read_mode_attribute_expect_success(self, endpoint, attribute): + cluster = Clusters.Objects.DishwasherMode + return await self.read_single_attribute_check_success(endpoint=endpoint, cluster=cluster, attribute=attribute) + + async def send_change_to_mode_cmd(self, newMode) -> Clusters.Objects.DishwasherMode.Commands.ChangeToModeResponse: + ret = await self.send_single_cmd(cmd=Clusters.Objects.DishwasherMode.Commands.ChangeToMode(newMode=newMode), endpoint=self.endpoint) + asserts.assert_true(type_matches(ret, Clusters.Objects.DishwasherMode.Commands.ChangeToModeResponse), + "Unexpected return type for ChangeToMode") + return ret + + async def write_on_mode(self, newMode): + ret = await self.default_controller.WriteAttribute(self.dut_node_id, [(self.endpoint, Clusters.DishwasherMode.Attributes.OnMode(newMode))]) + asserts.assert_equal(ret[0].Status, Status.Success, "Writing to OnMode failed") + + async def send_on_command_expect_success(self) -> Clusters.Objects.OnOff.Commands.On: + ret = await self.send_single_cmd(cmd=Clusters.Objects.OnOff.Commands.On(), endpoint=self.endpoint) + return ret + + async def send_off_command_expect_success(self) -> Clusters.Objects.OnOff.Commands.Off: + ret = await self.send_single_cmd(cmd=Clusters.Objects.OnOff.Commands.Off(), endpoint=self.endpoint) + return ret + + async def check_for_valid_mode(self, endpoint, mode): + attr = Clusters.DishwasherMode.Attributes.SupportedModes + supported_modes = await self.read_mode_attribute_expect_success(endpoint=endpoint, attribute=attr) + + if mode == NullValue: + logging.info("Mode is NULL") + return False + + mode_found = False + + for m in supported_modes: + if m.mode == mode: + mode_found = True + + return mode_found + + async def check_preconditions(self, endpoint): + logging.info("Checking preconditions...") + # Check to see if both the OnOff and DishwasherMode clusters are available on the same endpoint + cluster = Clusters.Objects.OnOff + attr = Clusters.OnOff.Attributes.OnOff + onOff = await self.read_single_attribute_check_success(endpoint=endpoint, cluster=cluster, attribute=attr) + logging.info("OnOff: %s" % (onOff)) + + # Verify that OnMode is non-null and a mode value from the supported modes attribute + cluster = Clusters.Objects.DishwasherMode + attr = Clusters.DishwasherMode.Attributes.OnMode + onMode = await self.read_single_attribute_check_success(endpoint=endpoint, cluster=cluster, attribute=attr) + logging.info("OnMode: %s" % (onMode)) + + cluster = Clusters.Objects.DishwasherMode + attr = Clusters.DishwasherMode.Attributes.SupportedModes + supported_modes = await self.read_mode_attribute_expect_success(endpoint=endpoint, attribute=attr) + + supported_modes_dut = [] + for m in supported_modes: + if m.mode in supported_modes_dut: + asserts.fail("SupportedModes must have unique mode values!") + else: + supported_modes_dut.append(m.mode) + + if onMode != NullValue and onMode in supported_modes_dut: + return True + + return False + + @async_test_body + async def test_TC_DISHM_3_1(self): + # Adding endpoint here to avoid definition in command line + self.endpoint = self.user_params.get("endpoint", 1) + logging.info("This test expects to find this cluster on endpoint 1") + + asserts.assert_true(self.check_pics("DISHM.S.A0000"), "DISHM.S.A0000 must be supported") + asserts.assert_true(self.check_pics("DISHM.S.A0001"), "DISHM.S.A0001 must be supported") + asserts.assert_true(self.check_pics("DISHM.S.C00.Rsp"), "DISHM.S.C00.Rsp must be supported") + asserts.assert_true(self.check_pics("DISHM.S.C01.Tx"), "DISHM.S.C01.Tx must be supported") + + if not self.check_pics("DISHM.S.A0003"): + logging.info("Test skipped because PICS DISHM.S.A0003 (OnMode) is not set") + return + + if not self.check_pics("DISHM.S.F00"): + logging.info("Test skipped because PICS DISHM.S.F00 (DepOnOff) is not set") + return + + ret = await self.check_preconditions(self.endpoint) + asserts.assert_true(ret, "invalid preconditions - OnMode is null or not set to a mode in the Supported Modes attribute") + + attributes = Clusters.DishwasherMode.Attributes + + from enum import Enum + + class CommonCodes(Enum): + SUCCESS = 0x00 + UNSUPPORTED_MODE = 0x01 + GENERIC_FAILURE = 0x02 + + self.print_step(1, "Commissioning, already done") + + self.print_step(2, "Read OnMode attribute") + # null check completed in precondition check + on_mode_dut = await self.read_mode_attribute_expect_success(endpoint=self.endpoint, attribute=attributes.OnMode) + + self.print_step(3, "Read CurrentMode attribute") + old_current_mode_dut = await self.read_mode_attribute_expect_success(endpoint=self.endpoint, attribute=attributes.CurrentMode) + mode_check = await self.check_for_valid_mode(endpoint=self.endpoint, mode=old_current_mode_dut) + asserts.assert_true(mode_check, "Current mode is NULL or is not a supported mode") + + if old_current_mode_dut == on_mode_dut: + + self.print_step(4, "Read SupportedModes attribute") + supported_modes_dut = await self.read_mode_attribute_expect_success(endpoint=self.endpoint, attribute=attributes.SupportedModes) + asserts.assert_true(isinstance(supported_modes_dut, list), "SupportedModes must be a list") + asserts.assert_greater_equal(len(supported_modes_dut), 2, "SupportedModes must have at least two entries!") + asserts.assert_true(isinstance( + supported_modes_dut[0], chip.clusters.Objects.DishwasherMode.Structs.ModeOptionStruct), "SupportedModes must must contain ModeOptionStructs") + + for m in supported_modes_dut: + if m.mode != on_mode_dut: + new_mode_th = m.mode + break + + self.print_step(5, "Send ChangeToMode command with NewMode set to %d" % (new_mode_th)) + ret = await self.send_change_to_mode_cmd(newMode=new_mode_th) + asserts.assert_true(ret.status == CommonCodes.SUCCESS.value, "Changing the mode should succeed") + + self.print_step(6, "Send Off command") + ret = await self.send_off_command_expect_success() + + self.print_step(7, "Send On command") + ret = await self.send_on_command_expect_success() + + self.print_step(8, "Read CurrentMode attribute") + current_mode = await self.read_mode_attribute_expect_success(endpoint=self.endpoint, attribute=attributes.CurrentMode) + asserts.assert_true(on_mode_dut == current_mode, "CurrentMode must match OnMode after a power cycle") + + +if __name__ == "__main__": + default_matter_test_main() diff --git a/src/python_testing/TC_DISHM_3_2.py b/src/python_testing/TC_DISHM_3_2.py new file mode 100644 index 00000000000000..c91c314b975a18 --- /dev/null +++ b/src/python_testing/TC_DISHM_3_2.py @@ -0,0 +1,170 @@ +# +# Copyright (c) 2023 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 + +import chip.clusters as Clusters +from chip.clusters.Types import NullValue +from chip.interaction_model import Status +from chip.tlv import uint +from matter_testing_support import MatterBaseTest, async_test_body, default_matter_test_main, type_matches +from mobly import asserts + + +class TC_DISHM_3_2(MatterBaseTest): + + async def read_mode_attribute_expect_success(self, endpoint, attribute): + cluster = Clusters.Objects.DishwasherMode + return await self.read_single_attribute_check_success(endpoint=endpoint, cluster=cluster, attribute=attribute) + + async def send_change_to_mode_cmd(self, newMode) -> Clusters.Objects.DishwasherMode.Commands.ChangeToModeResponse: + ret = await self.send_single_cmd(cmd=Clusters.Objects.DishwasherMode.Commands.ChangeToMode(newMode=newMode), endpoint=self.endpoint) + asserts.assert_true(type_matches(ret, Clusters.Objects.DishwasherMode.Commands.ChangeToModeResponse), + "Unexpected return type for ChangeToMode") + return ret + + async def write_start_up_mode(self, newMode): + ret = await self.default_controller.WriteAttribute(self.dut_node_id, [(self.endpoint, Clusters.DishwasherMode.Attributes.StartUpMode(newMode))]) + logging.info("Write StartUpMode Return: %s" % ret[0]) + asserts.assert_equal(ret[0].Status, Status.Success, "Writing to StartUpMode failed") + + async def check_preconditions(self, endpoint): + # check whether the StartUpMode will be overridden by the OnMode attribute + + if not self.check_pics("DISHM.S.F00"): + return True + + logging.info("DISHM.S.F00: 1") + + cluster = Clusters.Objects.OnOff + attr = Clusters.OnOff.Attributes.StartUpOnOff + startUpOnOff = await self.read_single_attribute_check_success(endpoint=endpoint, cluster=cluster, attribute=attr) + logging.info("StartUpOnOff: %s" % (startUpOnOff)) + if startUpOnOff == NullValue or startUpOnOff == 0: + return True + + cluster = Clusters.Objects.DishwasherMode + attr = Clusters.DishwasherMode.Attributes.OnMode + onMode = await self.read_single_attribute_check_success(endpoint=endpoint, cluster=cluster, attribute=attr) + logging.info("OnMode: %s" % (onMode)) + if onMode == NullValue: + return True + + return False + + @async_test_body + async def test_TC_DISHM_3_2(self): + + self.endpoint = self.user_params.get("endpoint", 1) + logging.info("This test expects to find this cluster on endpoint 1") + + asserts.assert_true(self.check_pics("DISHM.S.A0000"), "DISHM.S.A0000 must be supported") + asserts.assert_true(self.check_pics("DISHM.S.A0001"), "DISHM.S.A0001 must be supported") + asserts.assert_true(self.check_pics("DISHM.S.C00.Rsp"), "DISHM.S.C00.Rsp must be supported") + asserts.assert_true(self.check_pics("DISHM.S.C01.Tx"), "DISHM.S.C01.Tx must be supported") + + if not self.check_pics("DISHM.S.A0002"): + logging.info("Test skipped because PICS DISHM.S.A0002 (StartupMode) is not set") + return + + depOnOffKey = "DISHM.S.F00" + asserts.assert_true(depOnOffKey in self.matter_test_config.pics, "%s must be provided" % (depOnOffKey)) + + ret = await self.check_preconditions(self.endpoint) + asserts.assert_true(ret, "invalid preconditions - StartUpMode overridden by OnMode") + attributes = Clusters.DishwasherMode.Attributes + + from enum import Enum + + class CommonCodes(Enum): + SUCCESS = 0x00 + UNSUPPORTED_MODE = 0x01 + GENERIC_FAILURE = 0x02 + + self.print_step(1, "Commissioning, already done") + + self.print_step(2, "Read StartUpMode attribute") + + startup_mode_dut = await self.read_mode_attribute_expect_success(endpoint=self.endpoint, attribute=attributes.StartUpMode) + + logging.info("StartUpMode: %s" % (startup_mode_dut)) + + asserts.assert_true(type_matches(startup_mode_dut, uint) or startup_mode_dut == + NullValue, "Startup mode value should be an integer value or null") + + if startup_mode_dut == NullValue: + + self.print_step(3, "Read SupportedModes attribute") + supported_modes_dut = await self.read_mode_attribute_expect_success(endpoint=self.endpoint, attribute=attributes.SupportedModes) + + logging.info("SupportedModes: %s" % (supported_modes_dut)) + + asserts.assert_greater_equal(len(supported_modes_dut), 2, "SupportedModes must have at least two entries!") + + for m in supported_modes_dut: + new_start_up_mode_th = m.mode + break + + self.print_step(4, "Write to the StartUpMode Attribute") + await self.write_start_up_mode(newMode=new_start_up_mode_th) + + else: + new_start_up_mode_th = startup_mode_dut + + self.print_step(5, "Read CurrentMode attribute") + + old_current_mode_dut = await self.read_mode_attribute_expect_success(endpoint=self.endpoint, attribute=attributes.CurrentMode) + self.default_controller.ExpireSessions(self.dut_node_id) + + if old_current_mode_dut == startup_mode_dut: + self.print_step(6, "Read SupportedModes attribute") + supported_modes_dut = await self.read_mode_attribute_expect_success(endpoint=self.endpoint, attribute=attributes.SupportedModes) + + logging.info("SupportedModes: %s" % (supported_modes_dut)) + + asserts.assert_greater_equal(len(supported_modes_dut), 2, "SupportedModes must have at least two entries!") + + for m in supported_modes_dut: + if m.mode != startup_mode_dut: + new_mode_th = m.mode + break + + self.print_step(7, "Send ChangeToMode command with NewMode set to %d" % (new_mode_th)) + + ret = await self.send_change_to_mode_cmd(newMode=new_mode_th) + asserts.assert_true(ret.status == CommonCodes.SUCCESS.value, "Changing the mode should succeed") + + self.print_step(8, "Physically power cycle the device") + input("Press Enter when done.\n") + + self.print_step(9, "Read StartUpMode attribute") + + new_start_up_mode_dut = await self.read_mode_attribute_expect_success(endpoint=self.endpoint, attribute=attributes.StartUpMode) + + logging.info("StartUpMode: %s" % (new_start_up_mode_dut)) + asserts.assert_true(type_matches(new_start_up_mode_dut, uint), "StartUpMode must be an integer type") + asserts.assert_true(new_start_up_mode_dut == new_start_up_mode_th, "CurrentMode must match StartUpMode after a power cycle") + + self.print_step(10, "Read CurrentMode attribute") + + current_mode = await self.read_mode_attribute_expect_success(endpoint=self.endpoint, attribute=attributes.CurrentMode) + logging.info("CurrentMode: %s" % (current_mode)) + asserts.assert_true(new_start_up_mode_dut == current_mode, "CurrentMode must match StartUpMode after a power cycle") + + +if __name__ == "__main__": + default_matter_test_main() diff --git a/src/python_testing/TC_DISHM_3_3.py b/src/python_testing/TC_DISHM_3_3.py new file mode 100644 index 00000000000000..dfdde5ef4cb73f --- /dev/null +++ b/src/python_testing/TC_DISHM_3_3.py @@ -0,0 +1,176 @@ +# +# Copyright (c) 2023 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 + +import chip.clusters as Clusters +from chip.clusters.Types import NullValue +from chip.interaction_model import Status +from chip.tlv import uint +from matter_testing_support import MatterBaseTest, async_test_body, default_matter_test_main, type_matches +from mobly import asserts + + +class TC_DISHM_3_3(MatterBaseTest): + + async def read_mode_attribute_expect_success(self, endpoint, attribute): + cluster = Clusters.Objects.DishwasherMode + return await self.read_single_attribute_check_success(endpoint=endpoint, cluster=cluster, attribute=attribute) + + async def send_change_to_mode_cmd(self, newMode) -> Clusters.Objects.DishwasherMode.Commands.ChangeToModeResponse: + ret = await self.send_single_cmd(cmd=Clusters.Objects.DishwasherMode.Commands.ChangeToMode(newMode=newMode), endpoint=self.endpoint) + asserts.assert_true(type_matches(ret, Clusters.Objects.DishwasherMode.Commands.ChangeToModeResponse), + "Unexpected return type for ChangeToMode") + return ret + + async def write_on_mode_expect_success(self, newMode): + ret = await self.default_controller.WriteAttribute(self.dut_node_id, [(self.endpoint, Clusters.DishwasherMode.Attributes.OnMode(newMode))]) + asserts.assert_equal(ret[0].Status, Status.Success, "Writing to OnMode failed") + return ret + + async def write_startup_onoff(self) -> Clusters.Objects.OnOff.Commands.Off: + ret = await self.default_controller.WriteAttribute(self.dut_node_id, [(self.endpoint, Clusters.OnOff.Attributes.StartUpOnOff(1))]) + asserts.assert_equal(ret[0].Status, Status.Success, "Writing to OnOff failed") + return ret + + @async_test_body + async def test_TC_DISHM_3_3(self): + + self.endpoint = self.user_params.get("endpoint", 1) + logging.info("This test expects to find this cluster on endpoint 1") + + asserts.assert_true(self.check_pics("DISHM.S.A0000"), "DISHM.S.A0000 must be supported") + asserts.assert_true(self.check_pics("DISHM.S.A0001"), "DISHM.S.A0001 must be supported") + asserts.assert_true(self.check_pics("DISHM.S.C00.Rsp"), "DISHM.S.C00.Rsp must be supported") + asserts.assert_true(self.check_pics("DISHM.S.C01.Tx"), "DISHM.S.C01.Tx must be supported") + + if not self.check_pics("DISHM.S.A0002"): + logging.info("Test skipped because PICS DISHM.S.A0002 (StartupMode) is not set") + return + + if not self.check_pics("DISHM.S.A0003"): + logging.info("Test skipped because PICS DISHM.S.A0003 (OnMode) is not set") + return + + if not self.check_pics("DISHM.S.F00"): + logging.info("Test skipped because PICS DISHM.S.F00 (DepOnOff) is not set") + return + attributes = Clusters.DishwasherMode.Attributes + + from enum import Enum + + class CommonCodes(Enum): + SUCCESS = 0x00 + UNSUPPORTED_MODE = 0x01 + GENERIC_FAILURE = 0x02 + + self.print_step(1, "Commissioning, already done") + + self.print_step(2, "Read StartUpMode attribute") + + startup_mode_dut = await self.read_mode_attribute_expect_success(endpoint=self.endpoint, attribute=attributes.StartUpMode) + + logging.info("StartUpMode: %s" % (startup_mode_dut)) + + asserts.assert_true(type_matches(startup_mode_dut, uint) or startup_mode_dut == + NullValue, "Startup mode value should be an integer value or null") + + if startup_mode_dut == NullValue: + + self.print_step(3, "Read SupportedModes attribute") + supported_modes_dut = await self.read_mode_attribute_expect_success(endpoint=self.endpoint, attribute=attributes.SupportedModes) + + logging.info("SupportedModes: %s" % (supported_modes_dut)) + + asserts.assert_greater_equal(len(supported_modes_dut), 2, "SupportedModes must have at least two entries!") + + for m in supported_modes_dut: + new_start_up_mode_th = m.mode + break + + self.print_step(4, "Write to the StartUpMode Attribute") + + ret = await self.write_start_up_mode(newMode=new_start_up_mode_th) + # There is no recorded response returned from StartUpMode when writing to StartUpMode. + logging.info("Write StartUpMode Return: %s" % ret) + + else: + new_start_up_mode_th = startup_mode_dut + + self.print_step(5, "Read OnMode attribute") + + old_on_mode_dut = await self.read_mode_attribute_expect_success(endpoint=self.endpoint, attribute=attributes.OnMode) + + logging.info("OnMode: %s" % (old_on_mode_dut)) + + asserts.assert_true(type_matches(old_on_mode_dut, uint), "On mode value should be an integer value") + + if old_on_mode_dut == startup_mode_dut: + + self.print_step(6, "Read SupportedModes attribute") + supported_modes_dut = await self.read_mode_attribute_expect_success(endpoint=self.endpoint, attribute=attributes.SupportedModes) + + logging.info("SupportedModes: %s" % (supported_modes_dut)) + + asserts.assert_greater_equal(len(supported_modes_dut), 2, "SupportedModes must have at least two entries!") + + for m in supported_modes_dut: + if m.mode != startup_mode_dut: + new_mode_th = m.mode + break + + self.print_step(7, "Write to OnMode attribute with NewMode set to %d" % (new_mode_th)) + + ret = await self.write_on_mode_expect_success(newMode=new_mode_th) + + self.print_step(8, "Read OnMode attribute") + + new_on_mode_dut = await self.read_mode_attribute_expect_success(endpoint=self.endpoint, attribute=attributes.OnMode) + asserts.assert_true(type_matches(new_on_mode_dut, uint), "On mode value should be an integer value") + + logging.info("OnMode: %s" % (new_on_mode_dut)) + # There is an issue with the test plan here as new_mode_th cannot be read if step 7 is not executed so I've changed + # from comparing against new_mode_th to comparing against startup_mode_dut instead to ensure that it runs. + # Issue: https://github.com/CHIP-Specifications/chip-test-plans/issues/3478 + asserts.assert_true(startup_mode_dut != new_on_mode_dut, "OnMode must match the mode written in previous step") + + self.print_step(9, "Write to the StartUpOnOff attribute of OnOff cluster with a value of 1") + + ret = await self.write_startup_onoff() + + self.print_step(10, "Physically power cycle the device") + input("Press Enter when done.\n") + + self.print_step(11, "Read StartUpMode attribute from the DUT") + + startup_mode_dut = await self.read_mode_attribute_expect_success(endpoint=self.endpoint, attribute=attributes.StartUpMode) + + logging.info("StartUpMode: %s" % (startup_mode_dut)) + + asserts.assert_true(type_matches(startup_mode_dut, uint) and startup_mode_dut == + new_start_up_mode_th, "Startup mode value should be an integer value and match the StartUpMode value saved in step 3") + + self.print_step(12, "Read CurrentMode attribute") + current_mode = await self.read_mode_attribute_expect_success(endpoint=self.endpoint, attribute=attributes.CurrentMode) + + logging.info("CurrentMode: %s" % (current_mode)) + + asserts.assert_true(current_mode == new_on_mode_dut, "CurrentMode must match OnMode after a power cycle") + + +if __name__ == "__main__": + default_matter_test_main()