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
Show file tree
Hide file tree
Changes from 14 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
132 changes: 132 additions & 0 deletions src/python_testing/TC_CNET_4_3.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
#
# 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
# --PICS src/app/tests/suites/certification/ci-pics-values
# --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 pics_TC_CNET_4_3(self) -> list[str]:
"""Return the PICS definitions associated with this test."""
pics = [
"CNET.S.F02"
]
return pics

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"),
TestStep(3, "TH reads the MaxNetworks attribute from the DUT"),
TestStep(4, "TH reads the Networks attribute list from the DUT"),
TestStep(5, "TH reads InterfaceEnabled attribute from the DUT"),
TestStep(6, "TH reads LastNetworkingStatus attribute from the DUT"),
TestStep(7, "TH reads the LastNetworkID attribute from the DUT"),
TestStep(8, "TH reads the LastConnectErrorValue attribute from the DUT")
]
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, "All elements in list are of type NetworkInfoStruct",
Clusters.NetworkCommissioning.Structs.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)
asserts.assert_is(last_networking_status, NullValue, "Verify that LastNetworkingStatus attribute value is null")

self.step(7)
last_network_id = await self.read_single_attribute_check_success(
cluster=Clusters.NetworkCommissioning,
attribute=Clusters.NetworkCommissioning.Attributes.LastNetworkID)
asserts.assert_is(last_network_id, NullValue,
"Verify that the LastNetworkID attribute value is null")

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
Expand Up @@ -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

Expand Down Expand Up @@ -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], description: str, expected_type: Type[T]) -> None:
"""
Asserts that all elements in the list are of the expected type.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Loading