ServiceArea test scripts (#34548)
* initial commit
* fix bugs
* fix issues reported by the linter
* fix bug in checking for unique areaDesc
* add TC 1.5
* Update src/python_testing/TC_SEAR_1_2.py
Co-authored-by: William <hicklin@users.noreply.github.com>
* Update src/python_testing/TC_SEAR_1_2.py
Co-authored-by: William <hicklin@users.noreply.github.com>
* address code review comments
* fix issue introduced by the previous commit
* address code review feedback
* Update src/python_testing/TC_SEAR_1_2.py
Co-authored-by: Kiel Oleson <kielo@apple.com>
* address code review feedback
* remove PICS checked by the TC_SEAR_1.6
* more code review updates
* Restyled by autopep8
---------
Co-authored-by: William <hicklin@users.noreply.github.com>
Co-authored-by: Kiel Oleson <kielo@apple.com>
Co-authored-by: Restyled.io <commits@restyled.io>
diff --git a/src/python_testing/TC_SEAR_1_2.py b/src/python_testing/TC_SEAR_1_2.py
new file mode 100644
index 0000000..4ebb334
--- /dev/null
+++ b/src/python_testing/TC_SEAR_1_2.py
@@ -0,0 +1,368 @@
+#
+# 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.
+#
+
+# TODO - this was copied/pasted from another test, it needs to be reviewed and updated
+# 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
+# test-runner-run/run1/app: ${CHIP_RVC_APP}
+# test-runner-run/run1/factoryreset: True
+# test-runner-run/run1/quiet: True
+# test-runner-run/run1/app-args: --discriminator 1234 --KVS kvs1 --trace-to json:${TRACE_APP}.json
+# test-runner-run/run1/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:${TRACE_TEST_JSON}.json --trace-to perfetto:${TRACE_TEST_PERFETTO}.perfetto
+# === END CI TEST ARGUMENTS ===
+
+import logging
+from time import sleep
+
+import chip.clusters as Clusters
+from chip.clusters.Types import NullValue
+from matter_testing_support import MatterBaseTest, async_test_body, default_matter_test_main
+from mobly import asserts
+
+
+class TC_SEAR_1_2(MatterBaseTest):
+ def __init__(self, *args):
+ super().__init__(*args)
+ self.endpoint = None
+ self.is_ci = False
+ self.app_pipe = "/tmp/chip_rvc_fifo_"
+ self.mapid_list = []
+
+ # this must be kept in sync with the definitions from the Common Landmark Semantic Tag Namespace
+ self.MAX_LANDMARK_ID = 0x33
+
+ # this must be kept in sync with the definitions from the Common Relative Position Semantic Tag Namespace
+ self.MAX_RELPOS_ID = 0x07
+
+ async def read_sear_attribute_expect_success(self, endpoint, attribute):
+ cluster = Clusters.Objects.ServiceArea
+ return await self.read_single_attribute_check_success(endpoint=endpoint, cluster=cluster, attribute=attribute)
+
+ async def read_and_validate_supported_maps(self, step):
+ self.print_step(step, "Read SupportedMaps attribute")
+ supported_maps = await self.read_sear_attribute_expect_success(
+ endpoint=self.endpoint, attribute=Clusters.ServiceArea.Attributes.SupportedMaps)
+ logging.info("SupportedMaps: %s" % (supported_maps))
+ asserts.assert_less_equal(len(supported_maps), 255,
+ "SupportedMaps should have max 255 entries")
+
+ mapid_list = [m.mapID for m in supported_maps]
+ asserts.assert_true(len(set(mapid_list)) == len(mapid_list), "SupportedMaps must have unique MapID values!")
+
+ name_list = [m.name for m in supported_maps]
+ asserts.assert_true(len(set(name_list)) == len(name_list), "SupportedMaps must have unique Name values!")
+
+ # save so other methods can use this if neeeded
+ self.mapid_list = mapid_list
+
+ async def read_and_validate_supported_areas(self, step):
+ self.print_step(step, "Read SupportedAreas attribute")
+ supported_areas = await self.read_sear_attribute_expect_success(
+ endpoint=self.endpoint, attribute=Clusters.ServiceArea.Attributes.SupportedAreas)
+ logging.info("SupportedAreas: %s" % (supported_areas))
+ asserts.assert_less_equal(len(supported_areas), 255,
+ "SupportedAreas should have max 255 entries")
+ areaid_list = []
+ areadesc_s = set()
+ for a in supported_areas:
+ asserts.assert_true(a.areaID not in areaid_list, "SupportedAreas must have unique AreaID values!")
+
+ areaid_list.append(a.areaID)
+
+ if len(self.mapid_list) > 0:
+ asserts.assert_is_not(a.mapID, NullValue,
+ f"SupportedAreas entry with AreaID({a.areaID}) should not have null MapID")
+ asserts.assert_is(a.mapID in self.mapid_list,
+ f"SupportedAreas entry with AreaID({a.areaID}) has unknown MapID({a.mapID})")
+ k = f"mapID:{a.mapID} areaDesc:{a.areaDesc}"
+ asserts.assert_true(k not in areadesc_s,
+ f"SupportedAreas must have unique MapID({a.mapID}) + AreaDesc({a.areaDesc}) values!")
+ areadesc_s.add(k)
+ else:
+ # empty SupportedMaps
+ asserts.assert_is(a.mapID, NullValue,
+ f"SupportedAreas entry with AreaID({a.areaID}) should have null MapID")
+ k = f"areaDesc:{a.areaDesc}"
+ asserts.assert_true(k not in areadesc_s, f"SupportedAreas must have unique AreaDesc({a.areaDesc}) values!")
+ areadesc_s.add(k)
+
+ if a.locationInfo is NullValue and a.landmarkTag is NullValue:
+ asserts.assert_true(
+ f"SupportedAreas entry with AreaID({a.areaID}) should not have null LocationInfo and null LandmarkTag")
+ if a.landmarkTag is not NullValue:
+ asserts.assert_true(a.landmarkTag <= self.MAX_LANDMARK_ID,
+ f"SupportedAreas entry with AreaID({a.areaID}) has invalid LandmarkTag({a.landmarkTag})")
+ asserts.assert_true(a.positionTag is NullValue or a.positionTag in range(0, self.MAX_RELPOS_ID),
+ f"SupportedAreas entry with AreaID({a.areaID}) has invalid PositionTag({a.positionTag})")
+ # save so other methods can use this if neeeded
+ self.areaid_list = areaid_list
+
+ async def read_and_validate_selected_areas(self, step):
+ self.print_step(step, "Read SelectedAreas attribute")
+ selected_areas = await self.read_sear_attribute_expect_success(
+ endpoint=self.endpoint, attribute=Clusters.ServiceArea.Attributes.SelectedAreas)
+ logging.info(f"SelectedAreas {selected_areas}")
+
+ # TODO how to check if all entries are uint32?
+
+ asserts.assert_true(len(selected_areas) <= len(self.areaid_list),
+ f"SelectedAreas(len {len(selected_areas)}) should have at most {len(self.areaid_list)} entries")
+
+ asserts.assert_true(len(set(selected_areas)) == len(selected_areas), "SelectedAreas must have unique AreaID values!")
+
+ for a in selected_areas:
+ asserts.assert_true(a in self.areaid_list,
+ f"SelectedAreas entry {a} has invalid value")
+ # save so other methods can use this if neeeded
+ self.selareaid_list = selected_areas
+
+ async def read_and_validate_current_area(self, step):
+ self.print_step(step, "Read CurrentArea attribute")
+ current_area = await self.read_sear_attribute_expect_success(
+ endpoint=self.endpoint, attribute=Clusters.ServiceArea.Attributes.CurrentArea)
+ logging.info(f"CurrentArea {current_area}")
+
+ asserts.assert_true((len(self.selareaid_list) == 0 and current_area is NullValue)
+ or
+ current_area in self.selareaid_list,
+ f"CurrentArea {current_area} is invalid. SelectedAreas is {self.selareaid_list}.")
+ # save so other methods can use this if neeeded
+ self.current_area = current_area
+
+ async def read_and_validate_estimated_end_time(self, step):
+ import time
+ read_time = int(time.time())
+ self.print_step(step, "Read EstimatedEndTime attribute")
+ estimated_end_time = await self.read_sear_attribute_expect_success(
+ endpoint=self.endpoint, attribute=Clusters.ServiceArea.Attributes.EstimatedEndTime)
+ logging.info(f"EstimatedEndTime {estimated_end_time}")
+
+ if self.current_area is NullValue:
+ asserts.assert_true(estimated_end_time is NullValue,
+ "EstimatedEndTime should be null if CurrentArea is null.")
+ else:
+ # allow for some clock skew
+ asserts.assert_true(estimated_end_time >= read_time - 3*60,
+ f"EstimatedEndTime({estimated_end_time}) should be greater than the time when it was read({read_time})")
+
+ async def read_and_validate_progress(self, step):
+ self.print_step(step, "Read Progress attribute")
+ progress = await self.read_sear_attribute_expect_success(
+ endpoint=self.endpoint, attribute=Clusters.ServiceArea.Attributes.Progress)
+ logging.info(f"Progress {progress}")
+
+ asserts.assert_true(len(progress) <= len(self.areaid_list),
+ f"Progress(len {len(progress)}) should have at most {len(self.areaid_list)} entries")
+
+ progareaid_list = []
+ for p in progress:
+ if p.areaID in progareaid_list:
+ asserts.fail("Progress must have unique AreaID values!")
+ else:
+ progareaid_list.append(p.areaID)
+ asserts.assert_true(p.areaID in self.areaid_list,
+ f"Progress entry has invalid AreaID value ({p.areaID})")
+ asserts.assert_true(p.status in (Clusters.ServiceArea.OperationalStatusEnum.kPending,
+ Clusters.ServiceArea.OperationalStatusEnum.kOperating,
+ Clusters.ServiceArea.OperationalStatusEnum.kSkipped,
+ Clusters.ServiceArea.OperationalStatusEnum.kCompleted),
+ f"Progress entry has invalid Status value ({p.status})")
+ if p.status not in (Clusters.ServiceArea.OperationalStatusEnum.kSkipped, Clusters.ServiceArea.OperationalStatusEnum.kCompleted):
+ asserts.assert_true(p.totalOperationalTime is NullValue,
+ f"Progress entry should have a null TotalOperationalTime value (Status is {p.status})")
+ # TODO how to check that InitialTimeEstimate is either null or uint32?
+
+ # Sends and out-of-band command to the rvc-app
+ def write_to_app_pipe(self, command):
+ with open(self.app_pipe, "w") as app_pipe:
+ app_pipe.write(command + "\n")
+ # Allow some time for the command to take effect.
+ # This removes the test flakyness which is very annoying for everyone in CI.
+ sleep(0.001)
+
+ def TC_SEAR_1_2(self) -> list[str]:
+ return ["SEAR.S"]
+
+ @async_test_body
+ async def test_TC_SEAR_1_2(self):
+ self.endpoint = self.matter_test_config.endpoint
+ asserts.assert_false(self.endpoint is None, "--endpoint <endpoint> must be included on the command line in.")
+ self.is_ci = self.check_pics("PICS_SDK_CI_ONLY")
+ if self.is_ci:
+ app_pid = self.matter_test_config.app_pid
+ if app_pid == 0:
+ asserts.fail("The --app-pid flag must be set when PICS_SDK_CI_ONLY is set")
+ self.app_pipe = self.app_pipe + str(app_pid)
+
+ self.print_step(1, "Commissioning, already done")
+
+ # Ensure that the device is in the correct state
+ if self.is_ci:
+ self.write_to_app_pipe('{"Name": "Reset"}')
+
+ if self.check_pics("SEAR.S.F02"):
+ await self.read_and_validate_supported_maps(step=2)
+
+ await self.read_and_validate_supported_areas(step=3)
+
+ await self.read_and_validate_selected_areas(step=4)
+
+ if self.check_pics("SEAR.S.A0003"):
+ await self.read_and_validate_current_area(step=5)
+
+ if self.check_pics("SEAR.S.A0004"):
+ await self.read_and_validate_estimated_end_time(step=6)
+
+ if self.check_pics("SEAR.S.A0005"):
+ await self.read_and_validate_progress(step=7)
+
+ if self.check_pics("SEAR.S.F02") and self.check_pics("SEAR.S.M.REMOVE_MAP"):
+ test_step = "Manually ensure the SupportedMaps attribute is not empty and that the device is not operating"
+ self.print_step("8", test_step)
+ if not self.is_ci:
+ self.wait_for_user_input(prompt_msg=f"{test_step}, and press Enter when done.\n")
+
+ await self.read_and_validate_supported_maps(step=9)
+ old_supported_maps = self.mapid_list
+
+ test_step = "Manually intervene to remove one or more entries in the SupportedMaps list"
+ self.print_step("10", test_step)
+ if not self.is_ci:
+ self.wait_for_user_input(prompt_msg=f"{test_step}, and press Enter when done.\n")
+
+ await self.read_and_validate_supported_maps(step=11)
+ new_supported_maps = self.mapid_list
+ asserts.assert_true(len(old_supported_maps) > len(new_supported_maps), "Failed to remove map(s)")
+
+ # NOTE the following operations are all part of step 11 - read all these attributes and check the data consistency
+ # after removing map(s)
+ await self.read_and_validate_supported_areas(step=11)
+
+ await self.read_and_validate_selected_areas(step=11)
+
+ if self.check_pics("SEAR.S.A0003"):
+ await self.read_and_validate_current_area(step=11)
+
+ if self.check_pics("SEAR.S.A0004"):
+ await self.read_and_validate_estimated_end_time(step=11)
+
+ if self.check_pics("SEAR.S.A0005"):
+ await self.read_and_validate_progress(step=11)
+
+ if self.check_pics("SEAR.S.F02") and self.check_pics("SEAR.S.M.ADD_MAP"):
+ test_step = "Manually ensure the SupportedMaps attribute has less than 255 entries and that the device is not operating"
+ self.print_step("12", test_step)
+ if not self.is_ci:
+ self.wait_for_user_input(prompt_msg=f"{test_step}, and press Enter when done.\n")
+
+ await self.read_and_validate_supported_maps(step=13)
+ old_supported_maps = self.mapid_list
+
+ test_step = "Manually intervene to add one or more entries to the SupportedMaps list"
+ self.print_step("14", test_step)
+ if not self.is_ci:
+ self.wait_for_user_input(prompt_msg=f"{test_step}, and press Enter when done.\n")
+
+ await self.read_and_validate_supported_maps(step=15)
+ new_supported_maps = self.mapid_list
+ asserts.assert_true(len(old_supported_maps) < len(new_supported_maps), "Failed to add map(s)")
+
+ # NOTE the following operations are all part of step 15 - read all these attributes and check the data consistency
+ # after adding map(s)
+ await self.read_and_validate_supported_areas(step=15)
+
+ await self.read_and_validate_selected_areas(step=15)
+
+ if self.check_pics("SEAR.S.A0003"):
+ await self.read_and_validate_current_area(step=15)
+
+ if self.check_pics("SEAR.S.A0004"):
+ await self.read_and_validate_estimated_end_time(step=15)
+
+ if self.check_pics("SEAR.S.A0005"):
+ await self.read_and_validate_progress(step=15)
+
+ if self.check_pics("SEAR.S.M.REMOVE_AREA"):
+ test_step = "Manually ensure the SupportedAreas attribute is not empty and that the device is not operating"
+ self.print_step("16", test_step)
+ if not self.is_ci:
+ self.wait_for_user_input(prompt_msg=f"{test_step}, and press Enter when done.\n")
+
+ await self.read_and_validate_supported_areas(step=17)
+ old_supported_areas = self.areaid_list
+
+ test_step = "Manually intervene to remove one or more entries from the SupportedAreas list"
+ self.print_step("18", test_step)
+ if not self.is_ci:
+ self.wait_for_user_input(prompt_msg=f"{test_step}, and press Enter when done.\n")
+
+ await self.read_and_validate_supported_areas(step=19)
+ new_supported_areas = self.areaid_list
+ asserts.assert_true(len(old_supported_areas) > len(new_supported_areas), "Failed to remove area(s)")
+
+ # NOTE the following operations are all part of step 19 - read all these attributes and check the data consistency
+ # after removing areas(s)
+
+ await self.read_and_validate_selected_areas(step=19)
+
+ if self.check_pics("SEAR.S.A0003"):
+ await self.read_and_validate_current_area(step=19)
+
+ if self.check_pics("SEAR.S.A0004"):
+ await self.read_and_validate_estimated_end_time(step=19)
+
+ if self.check_pics("SEAR.S.A0005"):
+ await self.read_and_validate_progress(step=19)
+
+ if self.check_pics("SEAR.S.M.ADD_AREA"):
+ test_step = "Manually ensure the SupportedAreas attribute has less than 255 entries and that the device is not operating"
+ self.print_step("20", test_step)
+ if not self.is_ci:
+ self.wait_for_user_input(prompt_msg=f"{test_step}, and press Enter when done.\n")
+
+ await self.read_and_validate_supported_areas(step=21)
+ old_supported_areas = self.areaid_list
+
+ test_step = "Manually intervene to add one or more entries to the SupportedAreas list"
+ self.print_step("22", test_step)
+ if not self.is_ci:
+ self.wait_for_user_input(prompt_msg=f"{test_step}, and press Enter when done.\n")
+
+ await self.read_and_validate_supported_areas(step=23)
+ new_supported_areas = self.areaid_list
+ asserts.assert_true(len(old_supported_areas) < len(new_supported_areas), "Failed to add area(s)")
+
+ # NOTE the following operations are all part of step 23 - read all these attributes and check the data consistency
+ # after removing areas(s)
+
+ await self.read_and_validate_selected_areas(step=23)
+
+ if self.check_pics("SEAR.S.A0003"):
+ await self.read_and_validate_current_area(step=23)
+
+ if self.check_pics("SEAR.S.A0004"):
+ await self.read_and_validate_estimated_end_time(step=23)
+
+ if self.check_pics("SEAR.S.A0005"):
+ await self.read_and_validate_progress(step=23)
+
+
+if __name__ == "__main__":
+ default_matter_test_main()
diff --git a/src/python_testing/TC_SEAR_1_3.py b/src/python_testing/TC_SEAR_1_3.py
new file mode 100644
index 0000000..0acee1b
--- /dev/null
+++ b/src/python_testing/TC_SEAR_1_3.py
@@ -0,0 +1,155 @@
+#
+# 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.
+#
+
+# TODO - this was copied/pasted from abother test, it needs to be reviewed and updated
+# 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
+# test-runner-run/run1/app: ${CHIP_RVC_APP}
+# test-runner-run/run1/factoryreset: True
+# test-runner-run/run1/quiet: True
+# test-runner-run/run1/app-args: --discriminator 1234 --KVS kvs1 --trace-to json:${TRACE_APP}.json
+# test-runner-run/run1/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:${TRACE_TEST_JSON}.json --trace-to perfetto:${TRACE_TEST_PERFETTO}.perfetto
+# === END CI TEST ARGUMENTS ===
+
+import logging
+from time import sleep
+
+import chip.clusters as Clusters
+from matter_testing_support import MatterBaseTest, async_test_body, default_matter_test_main
+from mobly import asserts
+
+
+class TC_SEAR_1_3(MatterBaseTest):
+ def __init__(self, *args):
+ super().__init__(*args)
+ self.endpoint = None
+ self.is_ci = False
+ self.app_pipe = "/tmp/chip_rvc_fifo_"
+
+ async def read_sear_attribute_expect_success(self, endpoint, attribute):
+ cluster = Clusters.Objects.ServiceArea
+ return await self.read_single_attribute_check_success(endpoint=endpoint, cluster=cluster, attribute=attribute)
+
+ async def read_supported_areas(self, step):
+ self.print_step(step, "Read SupportedAreas attribute")
+ supported_areas = await self.read_sear_attribute_expect_success(
+ endpoint=self.endpoint, attribute=Clusters.ServiceArea.Attributes.SupportedAreas)
+ logging.info("SupportedAreas: %s" % (supported_areas))
+
+ return [a.areaID for a in supported_areas]
+
+ async def read_selected_areas(self, step):
+ self.print_step(step, "Read SelectedAreas attribute")
+ selected_areas = await self.read_sear_attribute_expect_success(
+ endpoint=self.endpoint, attribute=Clusters.ServiceArea.Attributes.SelectedAreas)
+ logging.info(f"SelectedAreas {selected_areas}")
+
+ return selected_areas
+
+ async def send_cmd_select_areas_expect_response(self, step, new_areas, expected_response):
+ self.print_step(step, f"Send SelectAreas command with NewAreas({new_areas})")
+ ret = await self.send_single_cmd(cmd=Clusters.Objects.ServiceArea.Commands.SelectAreas(newAreas=new_areas),
+ endpoint=self.endpoint)
+
+ asserts.assert_equal(ret.commandResponseState.errorStateID,
+ expected_response,
+ f"Command response ({ret.commandResponseState}) doesn't match the expected one")
+
+ # Sends and out-of-band command to the rvc-app
+ def write_to_app_pipe(self, command):
+ with open(self.app_pipe, "w") as app_pipe:
+ app_pipe.write(command + "\n")
+ # Allow some time for the command to take effect.
+ # This removes the test flakyness which is very annoying for everyone in CI.
+ sleep(0.001)
+
+ def TC_SEAR_1_3(self) -> list[str]:
+ return ["SEAR.S"]
+
+ @async_test_body
+ async def test_TC_SEAR_1_3(self):
+ self.endpoint = self.matter_test_config.endpoint
+ asserts.assert_false(self.endpoint is None, "--endpoint <endpoint> must be included on the command line in.")
+ self.is_ci = self.check_pics("PICS_SDK_CI_ONLY")
+ if self.is_ci:
+ app_pid = self.matter_test_config.app_pid
+ if app_pid == 0:
+ asserts.fail("The --app-pid flag must be set when PICS_SDK_CI_ONLY is set")
+ self.app_pipe = self.app_pipe + str(app_pid)
+
+ self.print_step(1, "Commissioning, already done")
+
+ # Ensure that the device is in the correct state
+ if self.is_ci:
+ self.write_to_app_pipe('{"Name": "Reset"}')
+
+ supported_area_ids = await self.read_supported_areas(step=2)
+ asserts.assert_true(len(self.supported_areas) > 0, "SupportedAreas is empty")
+ valid_area_id = supported_area_ids[0]
+ invalid_area_id = 1 + max(supported_area_ids)
+
+ duplicated_areas = [valid_area_id, valid_area_id]
+
+ # FIXME need to check if this is the correct name of this status code
+ await self.send_cmd_select_areas_expect_response(step=3, new_areas=duplicated_areas, expected_response=Clusters.ServiceArea.SelectAreasStatus.kDuplicatedAreas)
+
+ await self.send_cmd_select_areas_expect_response(step=4, new_areas=[], expected_response=Clusters.ServiceArea.SelectAreasStatus.kSuccess)
+
+ selected_areas = await self.read_selected_areas(step=5)
+ asserts.assert_true(len(selected_areas) == 0, "SelectedAreas should be empty")
+
+ await self.send_cmd_select_areas_expect_response(step=6, new_areas=[invalid_area_id], expected_response=Clusters.ServiceArea.SelectAreasStatus.kUnsupportedArea)
+
+ if self.check_pics("SEAR.S.M.INVALID_STATE_FOR_SELECT_AREAS") and self.check_pics("SEAR.S.M.HAS_MANUAL_SELAREA_STATE_CONTROL"):
+ test_step = "Manually intervene to put the device in a state that prevents it from executing the SelectAreas command"
+ self.print_step("7", test_step)
+ if not self.is_ci:
+ self.wait_for_user_input(prompt_msg=f"{test_step}, and press Enter when done.\n")
+
+ await self.send_cmd_select_areas_expect_response(step=8, new_areas=[valid_area_id], expected_response=Clusters.ServiceArea.SelectAreasStatus.kInvalidInMode)
+
+ if self.check_pics("SEAR.S.M.VALID_STATE_FOR_SELECT_AREAS") and self.check_pics("SEAR.S.M.HAS_MANUAL_SELAREA_STATE_CONTROL"):
+ test_step = f"Manually intervene to put the device in a state that allows it to execute the SelectAreas({supported_area_ids}) command"
+ self.print_step("9", test_step)
+ if not self.is_ci:
+ self.wait_for_user_input(prompt_msg=f"{test_step}, and press Enter when done.\n")
+
+ await self.send_cmd_select_areas_expect_response(step=10, new_areas=supported_area_ids, expected_response=Clusters.ServiceArea.SelectAreasStatus.kSuccess)
+
+ selected_areas = await self.read_selected_areas(step=11)
+ asserts.assert_true(len(selected_areas) == len(supported_area_ids),
+ f"SelectedAreas({selected_areas}) should match SupportedAreas({supported_area_ids})")
+
+ await self.send_cmd_select_areas_expect_response(step=12, new_areas=supported_area_ids, expected_response=Clusters.ServiceArea.SelectAreasStatus.kSuccess)
+
+ if self.check_pics("SEAR.S.M.VALID_STATE_FOR_SELECT_AREAS") and self.check_pics("SEAR.S.M.HAS_MANUAL_SELAREA_STATE_CONTROL") and self.check_pics("SEAR.S.M.SELECT_AREAS_WHILE_NON_IDLE"):
+ test_step = f"Manually intervene to put the device in a state that allows it to execute the SelectAreas({valid_area_id}) command, and put the device in a non-idle state"
+ self.print_step("13", test_step)
+ if not self.is_ci:
+ self.wait_for_user_input(prompt_msg=f"{test_step}, and press Enter when done.\n")
+
+ if self.check_pics("SEAR.S.F00"):
+ await self.send_cmd_select_areas_expect_response(step=14, new_areas=[valid_area_id], expected_response=Clusters.ServiceArea.SelectAreasStatus.kSuccess)
+ else:
+ await self.send_cmd_select_areas_expect_response(step=14, new_areas=[valid_area_id], expected_response=Clusters.ServiceArea.SelectAreasStatus.kInvalidInMode)
+
+
+if __name__ == "__main__":
+ default_matter_test_main()
diff --git a/src/python_testing/TC_SEAR_1_4.py b/src/python_testing/TC_SEAR_1_4.py
new file mode 100644
index 0000000..a632655
--- /dev/null
+++ b/src/python_testing/TC_SEAR_1_4.py
@@ -0,0 +1,84 @@
+#
+# 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.
+#
+
+# TODO - this was copied/pasted from abother test, it needs to be reviewed and updated
+# 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
+# test-runner-run/run1/app: ${CHIP_RVC_APP}
+# test-runner-run/run1/factoryreset: True
+# test-runner-run/run1/quiet: True
+# test-runner-run/run1/app-args: --discriminator 1234 --KVS kvs1 --trace-to json:${TRACE_APP}.json
+# test-runner-run/run1/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:${TRACE_TEST_JSON}.json --trace-to perfetto:${TRACE_TEST_PERFETTO}.perfetto
+# === END CI TEST ARGUMENTS ===
+
+import logging
+
+import chip.clusters as Clusters
+from matter_testing_support import MatterBaseTest, async_test_body, default_matter_test_main
+from mobly import asserts
+
+
+class TC_SEAR_1_4(MatterBaseTest):
+ def __init__(self, *args):
+ super().__init__(*args)
+ self.endpoint = None
+ self.is_ci = False
+ self.app_pipe = "/tmp/chip_rvc_fifo_"
+
+ async def read_sear_attribute_expect_success(self, endpoint, attribute):
+ cluster = Clusters.Objects.ServiceArea
+ return await self.read_single_attribute_check_success(endpoint=endpoint, cluster=cluster, attribute=attribute)
+
+ def TC_SEAR_1_4(self) -> list[str]:
+ return ["SEAR.S"]
+
+ @async_test_body
+ async def test_TC_SEAR_1_4(self):
+ self.endpoint = self.matter_test_config.endpoint
+ asserts.assert_false(self.endpoint is None, "--endpoint <endpoint> must be included on the command line in.")
+ self.is_ci = self.check_pics("PICS_SDK_CI_ONLY")
+ if self.is_ci:
+ app_pid = self.matter_test_config.app_pid
+ if app_pid == 0:
+ asserts.fail("The --app-pid flag must be set when PICS_SDK_CI_ONLY is set")
+ self.app_pipe = self.app_pipe + str(app_pid)
+
+ self.print_step(1, "Commissioning, already done")
+
+ # Ensure that the device is in the correct state
+ if self.is_ci:
+ self.write_to_app_pipe('{"Name": "Reset"}')
+
+ attribute_list = await self.read_sear_attribute_expect_success(
+ endpoint=self.endpoint, attribute=Clusters.ServiceArea.Attributes.AttributeList)
+ logging.info("AttributeList: %s" % (attribute_list))
+
+ if Clusters.ServiceArea.Attributes.CurrentArea not in attribute_list \
+ and Clusters.ServiceArea.Attributes.Progress not in attribute_list:
+
+ cmd_list = await self.read_sear_attribute_expect_success(
+ endpoint=self.endpoint, attribute=Clusters.ServiceArea.Attributes.AcceptedCommandList)
+ logging.info("AcceptedCommandList: %s" % (cmd_list))
+ asserts.assert_true(Clusters.ServiceArea.Commands.SkipArea not in cmd_list,
+ "SkipArea command should not be implemented if both CurrentArea and Progress are not")
+
+
+if __name__ == "__main__":
+ default_matter_test_main()
diff --git a/src/python_testing/TC_SEAR_1_5.py b/src/python_testing/TC_SEAR_1_5.py
new file mode 100644
index 0000000..adcf834
--- /dev/null
+++ b/src/python_testing/TC_SEAR_1_5.py
@@ -0,0 +1,283 @@
+#
+# 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.
+#
+
+# TODO - this was copied/pasted from abother test, it needs to be reviewed and updated
+# 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
+# test-runner-run/run1/app: ${CHIP_RVC_APP}
+# test-runner-run/run1/factoryreset: True
+# test-runner-run/run1/quiet: True
+# test-runner-run/run1/app-args: --discriminator 1234 --KVS kvs1 --trace-to json:${TRACE_APP}.json
+# test-runner-run/run1/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:${TRACE_TEST_JSON}.json --trace-to perfetto:${TRACE_TEST_PERFETTO}.perfetto
+# === END CI TEST ARGUMENTS ===
+
+import logging
+from time import sleep
+
+import chip.clusters as Clusters
+from chip.clusters.Types import NullValue
+from matter_testing_support import MatterBaseTest, async_test_body, default_matter_test_main
+from mobly import asserts
+
+
+class TC_SEAR_1_5(MatterBaseTest):
+ def __init__(self, *args):
+ super().__init__(*args)
+ self.endpoint = None
+ self.is_ci = False
+ self.app_pipe = "/tmp/chip_rvc_fifo_"
+
+ async def read_sear_attribute_expect_success(self, endpoint, attribute):
+ cluster = Clusters.Objects.ServiceArea
+ return await self.read_single_attribute_check_success(endpoint=endpoint, cluster=cluster, attribute=attribute)
+
+ async def read_supported_areas(self, step):
+ self.print_step(step, "Read SupportedAreas attribute")
+ supported_areas = await self.read_sear_attribute_expect_success(
+ endpoint=self.endpoint, attribute=Clusters.ServiceArea.Attributes.SupportedAreas)
+ logging.info("SupportedAreas: %s" % (supported_areas))
+
+ return [a.areaID for a in supported_areas]
+
+ async def read_selected_areas(self, step):
+ self.print_step(step, "Read SelectedAreas attribute")
+ selected_areas = await self.read_sear_attribute_expect_success(
+ endpoint=self.endpoint, attribute=Clusters.ServiceArea.Attributes.SelectedAreas)
+ logging.info(f"SelectedAreas {selected_areas}")
+
+ return selected_areas
+
+ async def read_progress(self, step):
+ self.print_step(step, "Read Progress attribute")
+ progress = await self.read_sear_attribute_expect_success(
+ endpoint=self.endpoint, attribute=Clusters.ServiceArea.Attributes.Progress)
+ logging.info(f"Progress {progress}")
+
+ return progress
+
+ async def read_current_area(self, step):
+ self.print_step(step, "Read CurrentArea attribute")
+ current_area = await self.read_sear_attribute_expect_success(
+ endpoint=self.endpoint, attribute=Clusters.ServiceArea.Attributes.CurrentArea)
+ logging.info(f"CurrentArea {current_area}")
+
+ return current_area
+
+ async def send_cmd_skip_area_expect_response(self, step, skipped_area, expected_response):
+ self.print_step(step, f"Send SkipArea command with SkippedArea({skipped_area})")
+ ret = await self.send_single_cmd(cmd=Clusters.Objects.ServiceArea.Commands.SkipArea(skippedArea=skipped_area),
+ endpoint=self.endpoint)
+
+ asserts.assert_equal(ret.commandResponseState.errorStateID,
+ expected_response,
+ f"Command response ({ret.commandResponseState}) doesn't match the expected one")
+
+ # Sends and out-of-band command to the rvc-app
+
+ def write_to_app_pipe(self, command):
+ with open(self.app_pipe, "w") as app_pipe:
+ app_pipe.write(command + "\n")
+ # Allow some time for the command to take effect.
+ # This removes the test flakyness which is very annoying for everyone in CI.
+ sleep(0.001)
+
+ def TC_SEAR_1_5(self) -> list[str]:
+ return ["SEAR.S", "SEAR.S.C02.Rsp"]
+
+ @async_test_body
+ async def test_TC_SEAR_1_5(self):
+ self.endpoint = self.matter_test_config.endpoint
+ asserts.assert_false(self.endpoint is None, "--endpoint <endpoint> must be included on the command line in.")
+ self.is_ci = self.check_pics("PICS_SDK_CI_ONLY")
+ if self.is_ci:
+ app_pid = self.matter_test_config.app_pid
+ if app_pid == 0:
+ asserts.fail("The --app-pid flag must be set when PICS_SDK_CI_ONLY is set")
+ self.app_pipe = self.app_pipe + str(app_pid)
+
+ self.print_step(1, "Commissioning, already done")
+
+ # Ensure that the device is in the correct state
+ if self.is_ci:
+ self.write_to_app_pipe('{"Name": "Reset"}')
+
+ supported_area_ids = await self.read_supported_areas(step=2)
+ asserts.assert_true(len(supported_area_ids) > 0, "SupportedAreas is empty")
+ valid_area_id = supported_area_ids[0]
+ invalid_area_id = 1 + max(supported_area_ids)
+
+ if self.check_pics("SEAR.S.M.INVALID_STATE_FOR_SKIP") and self.check_pics("SEAR.S.M.HAS_MANUAL_SKIP_STATE_CONTROL"):
+ test_step = "Manually intervene to put the device in a state that prevents it from executing the SkipArea command \
+ (e.g. set CurrentArea to null or make it not operate, i.e. be in the idle state)"
+ self.print_step("3", test_step)
+ if not self.is_ci:
+ self.wait_for_user_input(prompt_msg=f"{test_step}, and press Enter when done.\n")
+
+ await self.send_cmd_skip_area_expect_response(step=4, skipped_area=valid_area_id,
+ expected_response=Clusters.ServiceArea.SkipAreaStatus.kInvalidInMode)
+
+ if self.check_pics("SEAR.S.M.NO_SELAREA_FOR_SKIP") and self.check_pics("SEAR.S.M.HAS_MANUAL_SKIP_STATE_CONTROL"):
+ test_step = "Manually intervene to put the device in a state where the state would allow it to execute the SkipArea command, \
+ if SelectedAreas wasn't empty, and SelectedAreas is empty"
+ self.print_step("5", test_step)
+ if not self.is_ci:
+ self.wait_for_user_input(prompt_msg=f"{test_step}, and press Enter when done.\n")
+
+ await self.send_cmd_skip_area_expect_response(step=6, skipped_area=valid_area_id,
+ expected_response=Clusters.ServiceArea.SkipAreaStatus.kInvalidAreaList)
+
+ if self.check_pics("SEAR.S.M.VALID_STATE_FOR_SKIP") and self.check_pics("SEAR.S.M.HAS_MANUAL_SKIP_STATE_CONTROL"):
+ test_step = "Manually intervene to put the device in a state that allows it to execute the SkipArea command"
+ self.print_step("7", test_step)
+ if not self.is_ci:
+ self.wait_for_user_input(prompt_msg=f"{test_step}, and press Enter when done.\n")
+
+ await self.send_cmd_skip_area_expect_response(step=8, skipped_area=invalid_area_id,
+ expected_response=Clusters.ServiceArea.SkipAreaStatus.kInvalidSkippedArea)
+
+ if not self.check_pics("SEAR.S.M.VALID_STATE_FOR_SKIP"):
+ return
+
+ if self.check_pics("SEAR.S.A0005"):
+ old_progress_list = await self.read_progress(step=9)
+ asserts.assert_true(len(old_progress_list) > 0, f"len of Progress({len(old_progress_list)}) should not be zero)")
+
+ selected_areas = await self.read_selected_areas(step=10)
+ asserts.assert_true(len(selected_areas) > 0, "SelectedAreas is empty")
+
+ old_current_area = NullValue
+ if self.check_pics("SEAR.S.A0003"):
+ old_current_area = await self.read_current_area(step=11)
+
+ self.print_step("12", "")
+ if old_current_area is not NullValue:
+ await self.send_cmd_skip_area_expect_response(step=13, skipped_area=old_current_area,
+ expected_response=Clusters.ServiceArea.SkipAreaStatus.kSuccess)
+ if self.check_pics("SEAR.S.M.HAS_MANUAL_SKIP_STATE_CONTROL"):
+ test_step = "(Manual operation) wait for the device to skip the current area, and start operating at\
+ the next one it should process, or stop operating"
+ self.print_step("14", test_step)
+ if not self.is_ci:
+ self.wait_for_user_input(prompt_msg=f"{test_step}, and press Enter when done.\n")
+
+ if self.check_pics("SEAR.S.A0005"):
+ new_progress_list = await self.read_progress(step=15)
+ asserts.assert_true(len(new_progress_list) > 0,
+ f"len of Progress({len(new_progress_list)}) should not be zero)")
+
+ prog_areas = [p.areaID for p in new_progress_list]
+
+ asserts.assert_true(old_current_area in prog_areas, f"Progress should include area {old_current_area}")
+
+ new_current_area = await self.read_current_area(step=16)
+ for p in new_progress_list:
+ if p.areaID == old_current_area:
+ asserts.assert_true(p.status == Clusters.ServiceArea.OperationalStatusEnum.kSkipped,
+ "Progress for areaID({old_current_area}) should be Skipped")
+ break
+ test_step = "Indicate whether the device has stopped operating (y/n)"
+ ret = self.wait_for_user_input(prompt_msg=f"{test_step}, and press Enter when done.\n")
+
+ # Verify that if the device hasn't stopped operating, the `new_progress_list`'s entry matching `new_current_area` shows the Operating status
+ if ret != "y":
+ for p in new_progress_list:
+ if p.areaID == new_current_area:
+ asserts.assert_true(p.status == Clusters.ServiceArea.OperationalStatusEnum.kOperating,
+ "Progress for areaID({new_current_area}) should be Operating")
+ break
+
+ # next, we need to check for something else (so the condition of the 'if' above becomes part of the 'then' statement below):
+ # if before skipping all areas, except the current one, were Skipped or Completed, the device MUST have stopped operating
+ was_only_skipped_or_completed = True
+ for p in old_progress_list:
+ if p.areaID != old_current_area:
+ if p.status not in (Clusters.ServiceArea.OperationalStatusEnum.kSkipped,
+ Clusters.ServiceArea.OperationalStatusEnum.kCompleted):
+ was_only_skipped_or_completed = False
+ break
+ if was_only_skipped_or_completed:
+ asserts.assert_true(ret == "y", "The device should not be operating")
+
+ self.print_step("17", "")
+ return
+
+ if not self.check_pics("SEAR.S.A0005"):
+ return
+
+ if self.check_pics("SEAR.S.M.HAS_MANUAL_SKIP_STATE_CONTROL"):
+ test_step = "Manually intervene to put the device in a state that allows it to execute the SkipArea command"
+ self.print_step("18", test_step)
+ if not self.is_ci:
+ self.wait_for_user_input(prompt_msg=f"{test_step}, and press Enter when done.\n")
+
+ self.print_step("19", "")
+ if len(old_progress_list) == 0:
+ return
+
+ area_to_skip = NullValue
+ self.print_step("20", "")
+ for p in old_progress_list:
+ if p.status in (Clusters.ServiceArea.OperationalStatusEnum.kPending,
+ Clusters.ServiceArea.OperationalStatusEnum.kOperating):
+ area_to_skip = p.areaID
+ break
+
+ if area_to_skip is NullValue:
+ return
+
+ await self.send_cmd_skip_area_expect_response(step=21, skipped_area=area_to_skip,
+ expected_response=Clusters.ServiceArea.SkipAreaStatus.kSuccess)
+
+ if self.check_pics("SEAR.S.M.HAS_MANUAL_SKIP_STATE_CONTROL"):
+ test_step = "(Manual operation) wait for the device to update Progress or to stop operating"
+ self.print_step("22", test_step)
+ if not self.is_ci:
+ self.wait_for_user_input(prompt_msg=f"{test_step}, and press Enter when done.\n")
+
+ new_progress_list = await self.read_progress(step=23)
+ asserts.assert_true(len(new_progress_list) > 0, f"len of Progress({len(new_progress_list)}) should not be zero)")
+
+ for p in new_progress_list:
+ if p.areaID == area_to_skip:
+ asserts.assert_true(p.status == Clusters.ServiceArea.OperationalStatusEnum.kSkipped,
+ "Progress for areaID({new_current_area}) should be Skipped")
+ break
+
+ test_step = "Indicate whether the device has stopped operating (y/n)"
+ ret = self.wait_for_user_input(prompt_msg=f"{test_step}, and press Enter when done.\n")
+
+ was_only_skipped_or_completed = True
+ for p in old_progress_list:
+ if p.areaID != area_to_skip:
+ if p.status not in (Clusters.ServiceArea.OperationalStatusEnum.kSkipped,
+ Clusters.ServiceArea.OperationalStatusEnum.kCompleted):
+ was_only_skipped_or_completed = False
+ break
+ if was_only_skipped_or_completed:
+ asserts.assert_true(ret == "y", "The device should not be operating")
+ for p in new_progress_list:
+ if p.areaID == old_current_area:
+ asserts.assert_true(p.status == Clusters.ServiceArea.OperationalStatusEnum.kSkipped,
+ "Progress for areaID({old_current_area}) should be Skipped")
+ break
+
+
+if __name__ == "__main__":
+ default_matter_test_main()
diff --git a/src/python_testing/TC_SEAR_1_6.py b/src/python_testing/TC_SEAR_1_6.py
new file mode 100644
index 0000000..02a0fcd
--- /dev/null
+++ b/src/python_testing/TC_SEAR_1_6.py
@@ -0,0 +1,148 @@
+#
+# 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.
+#
+
+# TODO - this was copied/pasted from abother test, it needs to be reviewed and updated
+# 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
+# test-runner-run/run1/app: ${CHIP_RVC_APP}
+# test-runner-run/run1/factoryreset: True
+# test-runner-run/run1/quiet: True
+# test-runner-run/run1/app-args: --discriminator 1234 --KVS kvs1 --trace-to json:${TRACE_APP}.json
+# test-runner-run/run1/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:${TRACE_TEST_JSON}.json --trace-to perfetto:${TRACE_TEST_PERFETTO}.perfetto
+# === END CI TEST ARGUMENTS ===
+
+import logging
+from time import sleep
+
+import chip.clusters as Clusters
+from chip.clusters.Types import NullValue
+from matter_testing_support import MatterBaseTest, async_test_body, default_matter_test_main
+from mobly import asserts
+
+
+class TC_SEAR_1_6(MatterBaseTest):
+ def __init__(self, *args):
+ super().__init__(*args)
+ self.endpoint = None
+ self.is_ci = False
+ self.app_pipe = "/tmp/chip_rvc_fifo_"
+
+ async def read_sear_attribute_expect_success(self, endpoint, attribute):
+ cluster = Clusters.Objects.ServiceArea
+ return await self.read_single_attribute_check_success(endpoint=endpoint, cluster=cluster, attribute=attribute)
+
+ async def read_supported_areas(self, step):
+ self.print_step(step, "Read SupportedAreas attribute")
+ supported_areas = await self.read_sear_attribute_expect_success(
+ endpoint=self.endpoint, attribute=Clusters.ServiceArea.Attributes.SupportedAreas)
+ logging.info("SupportedAreas: %s" % (supported_areas))
+
+ return [a.areaID for a in supported_areas]
+
+ async def read_selected_areas(self, step):
+ self.print_step(step, "Read SelectedAreas attribute")
+ selected_areas = await self.read_sear_attribute_expect_success(
+ endpoint=self.endpoint, attribute=Clusters.ServiceArea.Attributes.SelectedAreas)
+ logging.info(f"SelectedAreas {selected_areas}")
+
+ return selected_areas
+
+ async def read_progress(self, step):
+ self.print_step(step, "Read Progress attribute")
+ progress = await self.read_sear_attribute_expect_success(
+ endpoint=self.endpoint, attribute=Clusters.ServiceArea.Attributes.Progress)
+ logging.info(f"Progress {progress}")
+
+ return progress
+
+ # Sends and out-of-band command to the rvc-app
+ def write_to_app_pipe(self, command):
+ with open(self.app_pipe, "w") as app_pipe:
+ app_pipe.write(command + "\n")
+ # Allow some time for the command to take effect.
+ # This removes the test flakyness which is very annoying for everyone in CI.
+ sleep(0.001)
+
+ def TC_SEAR_1_6(self) -> list[str]:
+ return ["SEAR.S", "SEAR.S.A0005", "SEAR.S.A0000", "SEAR.S.A0002", "SEAR.S.M.HAS_MANUAL_OPERATING_STATE_CONTROL"]
+
+ @async_test_body
+ async def test_TC_SEAR_1_6(self):
+ self.endpoint = self.matter_test_config.endpoint
+ asserts.assert_false(self.endpoint is None, "--endpoint <endpoint> must be included on the command line in.")
+ self.is_ci = self.check_pics("PICS_SDK_CI_ONLY")
+ if self.is_ci:
+ app_pid = self.matter_test_config.app_pid
+ if app_pid == 0:
+ asserts.fail("The --app-pid flag must be set when PICS_SDK_CI_ONLY is set")
+ self.app_pipe = self.app_pipe + str(app_pid)
+
+ self.print_step(1, "Commissioning, already done")
+
+ # Ensure that the device is in the correct state
+ if self.is_ci:
+ self.write_to_app_pipe('{"Name": "Reset"}')
+
+ test_step = "Manually intervene to put the device in the idle state and ensure SupportedAreas and SelectedAreas are not empty"
+ self.print_step("2", test_step)
+ if not self.is_ci:
+ self.wait_for_user_input(prompt_msg=f"{test_step}, and press Enter when done.\n")
+
+ supported_area_ids = await self.read_supported_areas(step=3)
+ asserts.assert_true(len(supported_area_ids) > 0, "SupportedAreas is empty")
+
+ selected_areas = await self.read_selected_areas(step=4)
+ asserts.assert_true(len(selected_areas) > 0, "SelectedAreas is empty")
+
+ test_step = "Manually intervene to put the device in the operating state"
+ self.print_step("5", test_step)
+ if not self.is_ci:
+ self.wait_for_user_input(prompt_msg=f"{test_step}, and press Enter when done.\n")
+
+ progress_list_operating = await self.read_progress(step=6)
+ asserts.assert_true(len(selected_areas) == len(progress_list_operating),
+ f"len of SelectedAreas({len(selected_areas)}) should be equal to len of Progress({len(progress_list_operating)})")
+
+ for p in progress_list_operating:
+ asserts.assert_true(p.areaID in selected_areas, f"Progress entry with unknown AreaID({p.areaID})")
+ asserts.assert_true(p.status in (Clusters.ServiceArea.OperationalStatusEnum.kPending,
+ Clusters.ServiceArea.OperationalStatusEnum.kOperating),
+ f"Progress entry with unexpected Status({p.status})")
+ asserts.assert_true(p.TotalOperationalTime is NullValue, "Progress entry with non-null TotalOperationalTime")
+
+ test_step = "While all entries in Progress show the Pending or Operating status (i.e. \
+ before any area is skipped or completed), manually intervene to put the device \
+ in the idle state, by ending the operation unexpectedly (e.g. force an error)"
+ self.print_step("7", test_step)
+ if not self.is_ci:
+ self.wait_for_user_input(prompt_msg=f"{test_step}, and press Enter when done.\n")
+
+ progress_list_idle = await self.read_progress(step=8)
+ asserts.assert_true(len(selected_areas) == len(progress_list_idle),
+ f"len of SelectedAreas({len(selected_areas)}) should be equal to len of Progress({len(progress_list_idle)})")
+
+ for p in progress_list_idle:
+ asserts.assert_true(p.areaID in selected_areas, f"Progress entry with unknown AreaID({p.areaID})")
+ asserts.assert_true(p.status == Clusters.ServiceArea.OperationalStatusEnum.kSkipped,
+ f"Progress entry with unexpected Status({p.status})")
+
+
+if __name__ == "__main__":
+ default_matter_test_main()