Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

TC-CNET-4.3: Automate #37387

Open
wants to merge 28 commits into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
9b6bad1
read ServerList
khodya Jan 22, 2025
6d499b7
WIP step 4
khodya Jan 31, 2025
2b508ea
impl step 4
khodya Feb 1, 2025
1e4bf6a
impl step 5
khodya Feb 1, 2025
6d847a4
imple step 6
khodya Feb 1, 2025
94c09f1
impl step 7
khodya Feb 1, 2025
c26a9b9
impl step 8
khodya Feb 1, 2025
7cb1c4a
fix step 4
khodya Feb 1, 2025
098b8c3
Restyled by isort
restyled-commits Feb 4, 2025
71d816f
change year
khodya Feb 4, 2025
47d1133
fix step 7
khodya Feb 7, 2025
9fbc49f
decouple assert_all and assert_list_element_type
khodya Feb 10, 2025
5903c55
network list should not be empty
khodya Feb 10, 2025
2269801
fix argument order
khodya Feb 10, 2025
f44600c
Merge branch 'master' into tc_cent_4_3_automate
khodya Feb 13, 2025
83612cc
Merge branch 'master' into tc_cent_4_3_automate
khodya Feb 25, 2025
0982449
remove CI arguments for manual tests
khodya Feb 25, 2025
c11a8c2
Merge branch 'master' into tc_cent_4_3_automate
khodya Feb 25, 2025
6941fa4
Revert "remove CI arguments for manual tests"
khodya Feb 25, 2025
986ada6
impl step 7 correctly
khodya Feb 25, 2025
3778f06
remove PICS
khodya Feb 25, 2025
fce427d
add expected steps & fix steps 6-7
khodya Feb 26, 2025
50b53f4
Merge remote-tracking branch 'upstream/master' into tc_cent_4_3_automate
khodya Feb 26, 2025
f58f8a3
fix step 4
khodya Feb 27, 2025
59f663a
fix step 7
khodya Feb 27, 2025
7275d0c
disable test on CI
khodya Feb 28, 2025
b8d5985
Restyled by whitespace
restyled-commits Feb 28, 2025
4584068
Restyled by prettier-yaml
restyled-commits Feb 28, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
139 changes: 139 additions & 0 deletions src/python_testing/TC_CNET_4_3.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
#
# Copyright (c) 2025 Project CHIP Authors
# All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

# 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:
# app: ${ALL_CLUSTERS_APP}
# app-args: --discriminator 1234 --KVS kvs1 --trace-to json:${TRACE_APP}.json
# script-args: >
# --endpoint 0
# --storage-path admin_storage.json
# --commissioning-method on-network
# --discriminator 1234
# --passcode 20202021
# --trace-to json:${TRACE_TEST_JSON}.json
# --trace-to perfetto:${TRACE_TEST_PERFETTO}.perfetto
# factory-reset: true
# quiet: true
# === END CI TEST ARGUMENTS ===

import chip.clusters as Clusters
import test_plan_support
from chip.clusters.Types import NullValue
from chip.testing import matter_asserts
from chip.testing.matter_testing import MatterBaseTest, TestStep, default_matter_test_main, has_feature, run_if_endpoint_matches
from mobly import asserts


class TC_CNET_4_3(MatterBaseTest):

def desc_TC_CNET_4_3(self) -> str:
return "[TC-CNET-4.3] [Ethernet] Verification for attributes check [DUT-Server]"

def steps_TC_CNET_4_3(self) -> list[TestStep]:
steps = [
TestStep(1, test_plan_support.commission_if_required(), "", is_commissioning=True),
TestStep(2, "TH reads Descriptor Cluster from the DUT with EP0 TH reads ServerList from the DUT",
"Verify for the presence of an element with value 49 (0x0031) in the ServerList"),
TestStep(3, "TH reads the MaxNetworks attribute from the DUT",
"Verify that MaxNetworks attribute value is within a range of 1 to 255"),
TestStep(4, "TH reads the Networks attribute list from the DUT",
"Verify that each element in the Networks attribute list has the following fields: 'NetworkID', 'connected'.\n\
NetworkID field is of type octstr with a length range 1 to 32 \n\
The connected field is of type bool \n\
Verify that only one entry has connected status as TRUE \n\
Verify that the number of entries in the Networks attribute is less than or equal to 'MaxNetworksValue'"),
TestStep(5, "TH reads InterfaceEnabled attribute from the DUT", "Verify that InterfaceEnabled attribute value is true"),
TestStep(6, "TH reads LastNetworkingStatus attribute from the DUT",
"LastNetworkingStatus attribute value will be within any one of the following values \
Success, NetworkNotFound, OutOfRange, RegulatoryError, UnknownError, null"),
TestStep(7, "TH reads the LastNetworkID attribute from the DUT",
"Verify that LastNetworkID attribute matches the NetworkID value of one of the entries in the Networks attribute list"),
TestStep(8, "TH reads the LastConnectErrorValue attribute from the DUT",
"Verify that LastConnectErrorValue attribute value is null")
]
return steps

@run_if_endpoint_matches(has_feature(Clusters.NetworkCommissioning,
Clusters.NetworkCommissioning.Bitmaps.Feature.kEthernetNetworkInterface))
async def test_TC_CNET_4_3(self):
# Commissioning already done
self.step(1)

self.step(2)
server_list = await self.read_single_attribute_check_success(
cluster=Clusters.Descriptor,
attribute=Clusters.Descriptor.Attributes.ServerList)
asserts.assert_true(49 in server_list,
msg="Verify for the presence of an element with value 49 (0x0031) in the ServerList")

self.step(3)
max_networks_count = await self.read_single_attribute_check_success(
cluster=Clusters.NetworkCommissioning,
attribute=Clusters.NetworkCommissioning.Attributes.MaxNetworks)
matter_asserts.assert_int_in_range(max_networks_count, min_value=1, max_value=255, description="MaxNetworks")

self.step(4)
networks = await self.read_single_attribute_check_success(
cluster=Clusters.NetworkCommissioning,
attribute=Clusters.NetworkCommissioning.Attributes.Networks)
asserts.assert_true(networks, "NetworkInfoStruct list should not be empty")
matter_asserts.assert_list_element_type(networks, Clusters.NetworkCommissioning.Structs.NetworkInfoStruct,
"All elements in list are of type NetworkInfoStruct")
matter_asserts.assert_all(networks, lambda x: isinstance(x.networkID, bytes) and 1 <= len(x.networkID) <= 32,
"NetworkID field is an octet string within a length range 1 to 32")
connected_networks_count = sum(map(lambda x: x.connected, networks))
asserts.assert_equal(connected_networks_count, 1, "Verify that only one entry has connected status as TRUE")
asserts.assert_less_equal(len(networks), max_networks_count,
"Number of entries in the Networks attribute is less than or equal to 'MaxNetworksValue'")

self.step(5)
interface_enabled = await self.read_single_attribute_check_success(
cluster=Clusters.NetworkCommissioning,
attribute=Clusters.NetworkCommissioning.Attributes.InterfaceEnabled)
asserts.assert_true(interface_enabled, "Verify that InterfaceEnabled attribute value is true")

self.step(6)
last_networking_status = await self.read_single_attribute_check_success(
cluster=Clusters.NetworkCommissioning,
attribute=Clusters.NetworkCommissioning.Attributes.LastNetworkingStatus)
expected_status = Clusters.NetworkCommissioning.Enums.NetworkCommissioningStatusEnum.kSuccess
asserts.assert_is(last_networking_status, expected_status, "Verify that LastNetworkingStatus attribute value is success")

self.step(7)
last_network_id = await self.read_single_attribute_check_success(
cluster=Clusters.NetworkCommissioning,
attribute=Clusters.NetworkCommissioning.Attributes.LastNetworkID)
matching_networks_count = sum(map(lambda x: x.networkID == last_network_id, networks))
asserts.assert_equal(matching_networks_count, 1,
"Verify that LastNetworkID attribute matches the NetworkID value of one of the entries")
asserts.assert_true(isinstance(last_network_id, bytes) and 1 <= len(last_network_id) <= 32,
"Verify LastNetworkID attribute value will be of type octstr with a length range of 1 to 32")

self.step(8)
last_connect_error_value = await self.read_single_attribute_check_success(
cluster=Clusters.NetworkCommissioning,
attribute=Clusters.NetworkCommissioning.Attributes.LastConnectErrorValue)
asserts.assert_is(last_connect_error_value, NullValue, "Verify that LastConnectErrorValue attribute value is null")


if __name__ == "__main__":
default_matter_test_main()
Original file line number Diff line number Diff line change
@@ -2,7 +2,7 @@
Matter-specific assertions building on top of Mobly asserts.
"""

from typing import Any, List, Optional, Type, TypeVar
from typing import Any, Callable, List, Optional, Type, TypeVar

from mobly import asserts

@@ -158,6 +158,23 @@ def assert_list(value: Any, description: str, min_length: Optional[int] = None,
f"{description} must not exceed {max_length} elements")


def assert_all(value: List[T], condition: Callable[[T], bool], description: str) -> None:
"""
Asserts that all elements in the list satisfy the provided condition.
Args:
value (List[T]): The list of elements to check.
condition (Callable[[T], bool]): A function that takes an element from value and returns True if it meets the condition, False otherwise.
description: User-defined description for error messages
Raises:
AssertionError: If any element in the list does not satisfy the condition.
"""
assert_list(value, description)
for i, item in enumerate(value):
asserts.assert_true(condition(item), f"Element at index {i} does not satisfy the condition: {description}")


def assert_list_element_type(value: List[Any], expected_type: Type[T], description: str, allow_empty: bool = False) -> None:
"""
Asserts that all elements in the list are of the expected type.
Original file line number Diff line number Diff line change
@@ -164,6 +164,18 @@ def test_assert_list(self):
with self.assertRaises(signals.TestFailure):
matter_asserts.assert_list([1, 2, 3], "test_max_length", max_length=2)

def test_assert_all(self):
"""Test assert_all with valid and invalid values."""
# Valid cases
matter_asserts.assert_all([], lambda x: isinstance(x, str), "empty list")
matter_asserts.assert_all([1, 2, 3], lambda x: isinstance(x, int), "list of ints")

# Invalid cases
with self.assertRaises(signals.TestFailure):
matter_asserts.assert_all([1, 2, 'a'], lambda x: isinstance(x, int), "mixed types")
with self.assertRaises(signals.TestFailure):
matter_asserts.assert_all("not a list", lambda x: True, "not a list")

def test_assert_list_element_type(self):
"""Test assert_list_element_type with valid and invalid values."""
# Valid cases
5 changes: 5 additions & 0 deletions src/python_testing/test_metadata.yaml
Original file line number Diff line number Diff line change
@@ -3,6 +3,11 @@
not_automated:
- name: MinimalRepresentation.py
reason: Code/Test not being used or not shared code for any other tests
- name: TC_CNET_4_3.py
reason:
network commissioning cluster does not return expected values on linux
example apps -
https://github.com/project-chip/connectedhomeip/issues/37824
- name: TC_CNET_4_4.py
reason: It has no CI execution block, is not executed in CI
- name: TC_DGGEN_3_2.py