Power Topology: add test scripts (#32114)

* Initial XML for Power Topology cluster

* Regen

* Restyled really wants newlines at the end of every JSON file

* Rename LeafTopology to TreeTopology

* [Feature] Power Topology server & all-clusters-app stub

* Make endpointId a constructor arg for PowerTopologyDelegate

* Change PowerTopologyDelegate to not return std::vectors

* Remove unneeded entries in attributeAccessInterfaceAttributes for Power Topology cluster

* Typo in python/chip/clusters/__init__.py

* Format zcl.json

* Add DynamicPowerFlow feature to PowerTopology stub

* Add Power Topology to client

* Set CI PICS values

* Python test script for Power Topology

* Linted python script

* Add Power Topology python script test

* Add PWRTL_1_1 to ciTests.json

* Restyled

* Regen

* Format PICS.yaml
diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml
index 05d2670..f083cd7 100644
--- a/.github/workflows/tests.yaml
+++ b/.github/workflows/tests.yaml
@@ -502,6 +502,7 @@
                   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_IDM_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 --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_IDM_1_4.py" --script-args "--hex-arg PIXIT.DGGEN.TEST_EVENT_TRIGGER_KEY:000102030405060708090a0b0c0d0e0f --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_IDM_4_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 --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_PWRTL_2_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" --script "src/python_testing/TC_RR_1_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" --script "src/python_testing/TC_RVCCLEANM_1_2.py" --script-args "--int-arg PIXIT_ENDPOINT:1 --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_RVCRUNM_1_2.py" --script-args "--int-arg PIXIT_ENDPOINT:1 --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"'
diff --git a/src/app/tests/suites/certification/PICS.yaml b/src/app/tests/suites/certification/PICS.yaml
index 068db7f..49554c3 100644
--- a/src/app/tests/suites/certification/PICS.yaml
+++ b/src/app/tests/suites/certification/PICS.yaml
@@ -10160,3 +10160,36 @@
 
     - label: "Can the mode change be manually controlled?"
       id: EEVSEM.S.M.CAN_MANUALLY_CONTROLLED
+
+    #
+    # Power Topology Cluster
+    #
+    - label: "Does the device implement the Power Topology Cluster as a server?"
+      id: PWRTL.S
+
+    #  Features
+
+    - label:
+          "Does the associated endpoint provide or consume power for the entire
+          node?"
+      id: PWRTL.S.F00
+
+    - label:
+          "Does the associated endpoint provide or consume power for itself and
+          its child endpoints?"
+      id: PWRTL.S.F01
+
+    - label:
+          "Does the associated endpoint provide or consume power for a provided
+          set of endpoints?"
+      id: PWRTL.S.F02
+
+    - label: "Can the provided set of endpoints change?"
+      id: PWRTL.S.F03
+
+    #Server attributes
+    - label: "Does the device implement the AvailableEndpoints attribute?"
+      id: PWRTL.S.A0000
+
+    - label: "Does the device implement the ActiveEndpoints attribute?"
+      id: PWRTL.S.A0001
diff --git a/src/app/tests/suites/certification/Test_TC_PWRTL_1_1.yaml b/src/app/tests/suites/certification/Test_TC_PWRTL_1_1.yaml
new file mode 100644
index 0000000..cfe6fd8
--- /dev/null
+++ b/src/app/tests/suites/certification/Test_TC_PWRTL_1_1.yaml
@@ -0,0 +1,119 @@
+# Copyright (c) 2021 Project CHIP Authors
+#
+# 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.
+# Auto-generated scripts for harness use only, please review before automation. The endpoints and cluster names are currently set to default
+
+name: 44.1.1. [TC-PWRTL-1.1] Global Attributes with DUT as Server
+
+PICS:
+    - PWRTL.S
+
+config:
+    nodeId: 0x12344321
+    cluster: "Power Topology"
+    endpoint: 1
+
+tests:
+    - label: "Step 1: Wait for the commissioned device to be retrieved"
+      cluster: "DelayCommands"
+      command: "WaitForCommissionee"
+      arguments:
+          values:
+              - name: "nodeId"
+                value: nodeId
+
+    - label: "Step 2: TH reads the ClusterRevision from DUT"
+      command: "readAttribute"
+      attribute: "ClusterRevision"
+      response:
+          value: 1
+          constraints:
+              type: int16u
+
+    - label:
+          "Step 3a: Given PWRTL.S.F00(Node) ensure featuremap has the correct
+          bit set"
+      PICS: PWRTL.S.F00
+      command: "readAttribute"
+      attribute: "FeatureMap"
+      response:
+          constraints:
+              type: bitmap32
+              hasMasksSet: [0x1]
+              hasMasksClear: [0x2, 0x4, 0x8]
+
+    - label:
+          "Step 3b: Given PWRTL.S.F01(Leaf) ensure featuremap has the correct
+          bit set"
+      PICS: PWRTL.S.F01
+      command: "readAttribute"
+      attribute: "FeatureMap"
+      response:
+          constraints:
+              type: bitmap32
+              hasMasksSet: [0x2]
+              hasMasksClear: [0x1, 0x4, 0x8]
+
+    - label:
+          "Step 3c: Given PWRTL.S.F02(Set) ensure featuremap has the correct bit
+          set"
+      PICS: PWRTL.S.F02
+      command: "readAttribute"
+      attribute: "FeatureMap"
+      response:
+          constraints:
+              type: bitmap32
+              hasMasksSet: [0x4]
+              hasMasksClear: [0x1, 0x2]
+
+    - label:
+          "Step 3d: Given PWRTL.S.F03(Dynamic Power Flow) ensure featuremap has
+          the correct bit set"
+      PICS: PWRTL.S.F03
+      command: "readAttribute"
+      attribute: "FeatureMap"
+      response:
+          constraints:
+              type: bitmap32
+              hasMasksSet: [0x4, 0x8]
+
+    - label: "Step 4a: TH reads AttributeList from DUT"
+      PICS: "!PICS_SF_SET && !PICS_SF_DYPF"
+      command: "readAttribute"
+      attribute: "AttributeList"
+      response:
+          constraints:
+              type: list
+              contains: []
+
+    - label:
+          "Step 4b: TH reads feature dependent attribute(AvailableEndpoints)
+          AttributeList from DUT"
+      PICS: "PICS_SF_SET && !PICS_SF_DYPF"
+      command: "readAttribute"
+      attribute: "AttributeList"
+      response:
+          constraints:
+              type: list
+              contains: [0]
+
+    - label:
+          "Step 4c: TH reads feature dependent attribute(ActiveEndpoints)
+          AttributeList from DUT"
+      PICS: "PICS_SF_SET && PICS_SF_DYPF"
+      command: "readAttribute"
+      attribute: "AttributeList"
+      response:
+          constraints:
+              type: list
+              contains: [0, 1]
diff --git a/src/app/tests/suites/certification/ci-pics-values b/src/app/tests/suites/certification/ci-pics-values
index c710f84..f200ad3 100644
--- a/src/app/tests/suites/certification/ci-pics-values
+++ b/src/app/tests/suites/certification/ci-pics-values
@@ -2939,3 +2939,15 @@
 #Manual controllable
 DEMM.S.M.CAN_TEST_MODE_FAILURE=1
 DEMM.S.M.CAN_MANUALLY_CONTROLLED=1
+
+#Power Topology Cluster
+# Server
+PWRTL.S=1
+PWRTL.S.A0000=1
+PWRTL.S.A0001=1
+
+#Features
+PWRTL.S.F00=0
+PWRTL.S.F01=0
+PWRTL.S.F02=1
+PWRTL.S.F03=1
\ No newline at end of file
diff --git a/src/app/tests/suites/ciTests.json b/src/app/tests/suites/ciTests.json
index ee6b543..26efd79 100644
--- a/src/app/tests/suites/ciTests.json
+++ b/src/app/tests/suites/ciTests.json
@@ -191,6 +191,7 @@
         "Test_TC_OO_2_4"
     ],
     "PowerSource": ["Test_TC_PS_1_1", "Test_TC_PS_2_1"],
+    "PowerTopology": ["Test_TC_PWRTL_1_1"],
     "PressureMeasurement": [
         "Test_TC_PRS_1_1",
         "Test_TC_PRS_2_1",
diff --git a/src/app/tests/suites/manualTests.json b/src/app/tests/suites/manualTests.json
index 774b3d8..d199a68 100644
--- a/src/app/tests/suites/manualTests.json
+++ b/src/app/tests/suites/manualTests.json
@@ -311,6 +311,7 @@
     "AccessControlEnforcement": [],
     "OvenMode": ["Test_TC_OTCCM_1_1", "Test_TC_OTCCM_1_2"],
     "EnergyEVSE": ["Test_TC_EEVSE_1_1", "Test_TC_EEVSE_2_1"],
+    "PowerTopology": ["Test_TC_PWRTL_1_1"],
     "collection": [
         "DeviceDiscovery",
         "Groups",
@@ -338,6 +339,7 @@
         "ModeSelect",
         "OTASoftwareUpdate",
         "PowerSourceConfiguration",
+        "PowerTopology",
         "PressureMeasurement",
         "SecureChannel",
         "SoftwareDiagnostics",
diff --git a/src/app/zap_cluster_list.json b/src/app/zap_cluster_list.json
index b399363..56adf0a 100644
--- a/src/app/zap_cluster_list.json
+++ b/src/app/zap_cluster_list.json
@@ -68,7 +68,6 @@
         "MESSAGES_CLUSTER": [],
         "MODE_SELECT_CLUSTER": [],
         "NETWORK_COMMISSIONING_CLUSTER": [],
-        "POWER_TOPOLOGY_CLUSTER": [],
         "SAMPLE_MEI_CLUSTER": [],
         "NITROGEN_DIOXIDE_CONCENTRATION_MEASUREMENT_CLUSTER": [],
         "OCCUPANCY_SENSING_CLUSTER": ["occupancy-sensor-server"],
@@ -91,6 +90,7 @@
         "POWER_PROFILE_CLUSTER": [],
         "POWER_SOURCE_CLUSTER": [],
         "POWER_SOURCE_CONFIGURATION_CLUSTER": [],
+        "POWER_TOPOLOGY_CLUSTER": [],
         "PRESSURE_MEASUREMENT_CLUSTER": [],
         "PROXY_CONFIGURATION_CLUSTER": [],
         "PROXY_DISCOVERY_CLUSTER": [],
diff --git a/src/python_testing/TC_PWRTL_2_1.py b/src/python_testing/TC_PWRTL_2_1.py
new file mode 100644
index 0000000..bd839cd
--- /dev/null
+++ b/src/python_testing/TC_PWRTL_2_1.py
@@ -0,0 +1,69 @@
+#
+#    Copyright (c) 2024 Project CHIP Authors
+#    All rights reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License");
+#    you may not use this file except in compliance with the License.
+#    You may obtain a copy of the License at
+#
+#        http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS,
+#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#    See the License for the specific language governing permissions and
+#    limitations under the License.
+#
+
+import logging
+
+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_PWRTL_2_1(MatterBaseTest):
+
+    def pics_TC_PWRTL_2_1(self) -> list[str]:
+        return ["PWRTL.S"]
+
+    @async_test_body
+    async def test_TC_PWRTL_2_1(self):
+
+        attributes = Clusters.PowerTopology.Attributes
+
+        endpoint = self.user_params.get("endpoint", 1)
+
+        self.print_step(1, "Commissioning, already done")
+
+        if not self.check_pics("PWRTL.S.A0000"):
+            logging.info("Test skipped because PICS PWRTL.S.A0000 is not set")
+            return
+
+        self.print_step(2, "Read AvailableAttributes attribute")
+        available_endpoints = await self.read_single_attribute_check_success(endpoint=endpoint, cluster=Clusters.Objects.PowerTopology, attribute=attributes.AvailableEndpoints)
+
+        if available_endpoints == NullValue:
+            logging.info("AvailableEndpoints is null")
+        else:
+            logging.info("AvailableEndpoints: %s" % (available_endpoints))
+
+            asserts.assert_less_equal(len(available_endpoints), 21,
+                                      "AvailableEndpoints length %d must be less than 21!" % len(available_endpoints))
+
+        if not self.check_pics("PWRTL.S.A0001"):
+            logging.info("Test skipped because PICS PWRTL.S.A0001 is not set")
+            return
+
+        self.print_step(3, "Read ActiveEndpoints attribute")
+        active_endpoints = await self.read_single_attribute_check_success(endpoint=endpoint, cluster=Clusters.Objects.PowerTopology,  attribute=attributes.ActiveEndpoints)
+        logging.info("ActiveEndpoints: %s" % (active_endpoints))
+
+        if available_endpoints == NullValue:
+            asserts.assert_true(active_endpoints == NullValue,
+                                "ActiveEndpoints should be null when AvailableEndpoints is null: %s" % active_endpoints)
+
+
+if __name__ == "__main__":
+    default_matter_test_main()