diff --git a/examples/all-clusters-app/all-clusters-common/src/wifi-diagnostics-stub.cpp b/examples/all-clusters-app/all-clusters-common/src/wifi-diagnostics-stub.cpp new file mode 100644 index 00000000000000..e142e897e40a15 --- /dev/null +++ b/examples/all-clusters-app/all-clusters-common/src/wifi-diagnostics-stub.cpp @@ -0,0 +1,96 @@ +/* + * + * Copyright (c) 2024 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. + */ + +#include +#include +#include +#include +#include + +#include +#include + +using namespace chip; +using namespace chip::app; +using namespace chip::DeviceLayer; +using namespace chip::app::Clusters; + +namespace { + +/** + * @brief Helper function to simulate a Disconnection event + */ +void SetTestEventTrigger_Disconnection() +{ + uint16_t reasonCode = 3; // Deauthenticated because sending STA is leaving (or has left) IBSS or ESS. + + WiFiDiagnosticsServer::Instance().OnDisconnectionDetected(reasonCode); +} + +/** + * @brief Helper function to simulate an Association Failure event + */ +void SetTestEventTrigger_AssociationFailure() +{ + uint8_t associationFailureCause = + static_cast(WiFiNetworkDiagnostics::AssociationFailureCauseEnum::kAuthenticationFailed); + uint16_t status = 4; // IEEE 802.11-2020 Status Codes, AP is unable to handle additional associated STAs + + WiFiDiagnosticsServer::Instance().OnAssociationFailureDetected(associationFailureCause, status); +} + +/** + * @brief Helper function to simulate a Connection Status event + */ +void SetTestEventTrigger_ConnectionStatus() +{ + uint8_t connectionStatus = static_cast(WiFiNetworkDiagnostics::ConnectionStatusEnum::kNotConnected); + WiFiDiagnosticsServer::Instance().OnConnectionStatusChanged(connectionStatus); +} + +} // anonymous namespace + +bool HandleWiFiDiagnosticsTestEventTrigger(uint64_t eventTrigger) +{ + // Convert raw trigger to our enum + WiFiDiagnosticsTrigger trigger = static_cast(eventTrigger); + + switch (trigger) + { + case WiFiDiagnosticsTrigger::kDisconnection: + ChipLogProgress(Support, "[WiFiDiagnostics-Test-Event] => Disconnection triggered"); + SetTestEventTrigger_Disconnection(); + break; + + case WiFiDiagnosticsTrigger::kAssociationFailure: + ChipLogProgress(Support, "[WiFiDiagnostics-Test-Event] => AssociationFailure triggered"); + SetTestEventTrigger_AssociationFailure(); + break; + + case WiFiDiagnosticsTrigger::kConnectionStatus: + ChipLogProgress(Support, "[WiFiDiagnostics-Test-Event] => ConnectionStatus triggered"); + SetTestEventTrigger_ConnectionStatus(); + break; + + default: + // If we get here, the trigger value is unknown to this handler + return false; + } + + // Indicate that we handled the trigger successfully + return true; +} diff --git a/examples/all-clusters-app/linux/BUILD.gn b/examples/all-clusters-app/linux/BUILD.gn index d13c51a6502cba..30c42e6cdf5b0c 100644 --- a/examples/all-clusters-app/linux/BUILD.gn +++ b/examples/all-clusters-app/linux/BUILD.gn @@ -60,6 +60,7 @@ source_set("chip-all-clusters-common") { "${chip_root}/examples/all-clusters-app/all-clusters-common/src/static-supported-modes-manager.cpp", "${chip_root}/examples/all-clusters-app/all-clusters-common/src/static-supported-temperature-levels.cpp", "${chip_root}/examples/all-clusters-app/all-clusters-common/src/tcc-mode.cpp", + "${chip_root}/examples/all-clusters-app/all-clusters-common/src/wifi-diagnostics-stub.cpp", "${chip_root}/examples/all-clusters-app/linux/diagnostic-logs-provider-delegate-impl.cpp", "${chip_root}/examples/energy-management-app/energy-management-common/common/src/EnergyTimeUtils.cpp", "${chip_root}/examples/energy-management-app/energy-management-common/device-energy-management/src/DeviceEnergyManagementDelegateImpl.cpp", diff --git a/examples/all-clusters-app/linux/args.gni b/examples/all-clusters-app/linux/args.gni index d98ceaa0eca989..068178d83b988c 100644 --- a/examples/all-clusters-app/linux/args.gni +++ b/examples/all-clusters-app/linux/args.gni @@ -31,3 +31,4 @@ chip_enable_smoke_co_trigger = true chip_enable_boolean_state_configuration_trigger = true chip_enable_water_heater_management_trigger = true chip_enable_software_diagnostics_trigger = true +chip_enable_wifi_diagnostics_trigger = true diff --git a/examples/platform/linux/AppMain.cpp b/examples/platform/linux/AppMain.cpp index c35e3e7db141ca..07ac781f3509b3 100644 --- a/examples/platform/linux/AppMain.cpp +++ b/examples/platform/linux/AppMain.cpp @@ -73,6 +73,9 @@ #if CHIP_DEVICE_CONFIG_ENABLE_SOFTWARE_DIAGNOSTIC_TRIGGER #include #endif +#if CHIP_DEVICE_CONFIG_ENABLE_WIFI_DIAGNOSTIC_TRIGGER +#include +#endif #if CHIP_DEVICE_CONFIG_ENABLE_OTA_REQUESTOR #include #endif @@ -592,6 +595,10 @@ void ChipLinuxAppMainLoop(AppMainLoopImplementation * impl) static SoftwareDiagnosticsTestEventTriggerHandler sSoftwareDiagnosticsTestEventTriggerHandler; sTestEventTriggerDelegate.AddHandler(&sSoftwareDiagnosticsTestEventTriggerHandler); #endif +#if CHIP_DEVICE_CONFIG_ENABLE_WIFI_DIAGNOSTIC_TRIGGER + static WiFiDiagnosticsTestEventTriggerHandler sWiFiDiagnosticsTestEventTriggerHandler; + sTestEventTriggerDelegate.AddHandler(&sWiFiDiagnosticsTestEventTriggerHandler); +#endif #if CHIP_DEVICE_CONFIG_ENABLE_OTA_REQUESTOR // We want to allow triggering OTA queries if OTA requestor is enabled static OTATestEventTriggerHandler sOtaTestEventTriggerHandler; diff --git a/examples/platform/linux/BUILD.gn b/examples/platform/linux/BUILD.gn index e2bb3a781e23b0..ec4659e6022689 100644 --- a/examples/platform/linux/BUILD.gn +++ b/examples/platform/linux/BUILD.gn @@ -26,6 +26,7 @@ if (current_os != "nuttx") { declare_args() { chip_enable_software_diagnostics_trigger = false + chip_enable_wifi_diagnostics_trigger = false chip_enable_smoke_co_trigger = false chip_enable_boolean_state_configuration_trigger = false chip_enable_energy_evse_trigger = false @@ -48,6 +49,10 @@ source_set("software-diagnostics-test-event-trigger") { sources = [ "${chip_root}/src/app/clusters/software-diagnostics-server/SoftwareDiagnosticsTestEventTriggerHandler.h" ] } +source_set("wifi-diagnostics-test-event-trigger") { + sources = [ "${chip_root}/src/app/clusters/wifi-network-diagnostics-server/WiFiDiagnosticsTestEventTriggerHandler.h" ] +} + source_set("smco-test-event-trigger") { sources = [ "${chip_root}/src/app/clusters/smoke-co-alarm-server/SmokeCOTestEventTriggerHandler.h" ] } @@ -100,6 +105,7 @@ source_set("app-main") { ":smco-test-event-trigger", ":software-diagnostics-test-event-trigger", ":water-heater-management-test-event-trigger", + ":wifi-diagnostics-test-event-trigger", "${chip_root}/src/data-model-providers/codegen:instance-header", "${chip_root}/src/lib", "${chip_root}/src/platform/logging:default", @@ -150,6 +156,7 @@ source_set("app-main") { "CHIP_DEVICE_CONFIG_ENABLE_ENERGY_EVSE_TRIGGER=${chip_enable_energy_evse_trigger}", "CHIP_DEVICE_CONFIG_ENABLE_ENERGY_REPORTING_TRIGGER=${chip_enable_energy_reporting_trigger}", "CHIP_DEVICE_CONFIG_ENABLE_WATER_HEATER_MANAGEMENT_TRIGGER=${chip_enable_water_heater_management_trigger}", + "CHIP_DEVICE_CONFIG_ENABLE_WIFI_DIAGNOSTIC_TRIGGER=${chip_enable_wifi_diagnostics_trigger}", "CHIP_DEVICE_CONFIG_ENABLE_DEVICE_ENERGY_MANAGEMENT_TRIGGER=${chip_enable_device_energy_management_trigger}", ] diff --git a/src/app/clusters/wifi-network-diagnostics-server/WiFiDiagnosticsTestEventTriggerHandler.h b/src/app/clusters/wifi-network-diagnostics-server/WiFiDiagnosticsTestEventTriggerHandler.h new file mode 100644 index 00000000000000..88785fb8abe530 --- /dev/null +++ b/src/app/clusters/wifi-network-diagnostics-server/WiFiDiagnosticsTestEventTriggerHandler.h @@ -0,0 +1,77 @@ +/* + * + * Copyright (c) 2024 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. + */ + +#pragma once + +#include +#include + +/** + * @brief User handler for handling the test event trigger + * + * @note If TestEventTrigger is enabled, it needs to be implemented in the app + * + * @param eventTrigger Event trigger to handle + * + * @retval true on success + * @retval false if error happened + */ +bool HandleWiFiDiagnosticsTestEventTrigger(uint64_t eventTrigger); + +namespace chip { + +/* + * These Test EventTrigger values are specified in the TC_DGWIFI test plan + * and are defined conditions used in test events. + * + * They are sent along with the enableKey (manufacturer defined secret) + * in the General Diagnostic cluster TestEventTrigger command + */ +enum class WiFiDiagnosticsTrigger : uint64_t +{ + // Simulate a disconnection via de-authentication or dis-association. + kDisconnection = 0x0036000000000000, + + // Force a scenario where the DUT exhausts all internal retries, + // and triggers an AssociationFailure event. + kAssociationFailure = 0x0036000000000001, + + // Simulate disconnecting and reconnecting the node’s Wi-Fi, + // so that a ConnectionStatus event is triggered. + kConnectionStatus = 0x0036000000000002, +}; + +class WiFiDiagnosticsTestEventTriggerHandler : public TestEventTriggerHandler +{ +public: + explicit WiFiDiagnosticsTestEventTriggerHandler() {} + + /** This function must return True if the eventTrigger is recognised and handled + * It must return False to allow a higher level TestEvent handler to check other + * clusters that may handle it. + */ + CHIP_ERROR HandleEventTrigger(uint64_t eventTrigger) override + { + if (HandleWiFiDiagnosticsTestEventTrigger(eventTrigger)) + { + return CHIP_NO_ERROR; + } + return CHIP_ERROR_INVALID_ARGUMENT; + } +}; + +} // namespace chip diff --git a/src/app/clusters/wifi-network-diagnostics-server/wifi-network-diagnostics-server.cpp b/src/app/clusters/wifi-network-diagnostics-server/wifi-network-diagnostics-server.cpp index 416c7179c10f60..90fc1af4f5aaa9 100644 --- a/src/app/clusters/wifi-network-diagnostics-server/wifi-network-diagnostics-server.cpp +++ b/src/app/clusters/wifi-network-diagnostics-server/wifi-network-diagnostics-server.cpp @@ -15,6 +15,7 @@ * limitations under the License. */ +#include "wifi-network-diagnostics-server.h" #include #include #include @@ -26,7 +27,6 @@ #include #include #include -#include #include #include @@ -240,70 +240,81 @@ CHIP_ERROR WiFiDiagosticsAttrAccess::Read(const ConcreteReadAttributePath & aPat return CHIP_NO_ERROR; } -class WiFiDiagnosticsDelegate : public DeviceLayer::WiFiDiagnosticsDelegate +} // anonymous namespace + +namespace chip { +namespace app { +namespace Clusters { + +WiFiDiagnosticsServer WiFiDiagnosticsServer::instance; + +/********************************************************** + * WiFiDiagnosticsServer Implementation + *********************************************************/ + +WiFiDiagnosticsServer & WiFiDiagnosticsServer::Instance() +{ + return instance; +} + +void WiFiDiagnosticsServer::OnDisconnectionDetected(uint16_t reasonCode) { - // Gets called when the Node detects Node’s Wi-Fi connection has been disconnected. - void OnDisconnectionDetected(uint16_t reasonCode) override + MATTER_TRACE_SCOPE("OnDisconnectionDetected", "WiFiDiagnosticsDelegate"); + ChipLogProgress(Zcl, "WiFiDiagnosticsDelegate: OnDisconnectionDetected"); + + for (auto endpoint : EnabledEndpointsWithServerCluster(WiFiNetworkDiagnostics::Id)) { - MATTER_TRACE_SCOPE("OnDisconnectionDetected", "WiFiDiagnosticsDelegate"); - ChipLogProgress(Zcl, "WiFiDiagnosticsDelegate: OnDisconnectionDetected"); + // If Wi-Fi Network Diagnostics cluster is implemented on this endpoint + Events::Disconnection::Type event{ reasonCode }; + EventNumber eventNumber; - for (auto endpoint : EnabledEndpointsWithServerCluster(WiFiNetworkDiagnostics::Id)) + if (CHIP_NO_ERROR != LogEvent(event, endpoint, eventNumber)) { - // If Wi-Fi Network Diagnostics cluster is implemented on this endpoint - Events::Disconnection::Type event{ reasonCode }; - EventNumber eventNumber; - - if (CHIP_NO_ERROR != LogEvent(event, endpoint, eventNumber)) - { - ChipLogError(Zcl, "WiFiDiagnosticsDelegate: Failed to record Disconnection event"); - } + ChipLogError(Zcl, "WiFiDiagnosticsDelegate: Failed to record Disconnection event"); } } +} - // Gets called when the Node fails to associate or authenticate an access point. - void OnAssociationFailureDetected(uint8_t associationFailureCause, uint16_t status) override - { - MATTER_TRACE_SCOPE("OnAssociationFailureDetected", "WiFiDiagnosticsDelegate"); - ChipLogProgress(Zcl, "WiFiDiagnosticsDelegate: OnAssociationFailureDetected"); +void WiFiDiagnosticsServer::OnAssociationFailureDetected(uint8_t associationFailureCause, uint16_t status) +{ + MATTER_TRACE_SCOPE("OnAssociationFailureDetected", "WiFiDiagnosticsDelegate"); + ChipLogProgress(Zcl, "WiFiDiagnosticsDelegate: OnAssociationFailureDetected"); - Events::AssociationFailure::Type event{ static_cast(associationFailureCause), status }; + Events::AssociationFailure::Type event{ static_cast(associationFailureCause), status }; - for (auto endpoint : EnabledEndpointsWithServerCluster(WiFiNetworkDiagnostics::Id)) - { - // If Wi-Fi Network Diagnostics cluster is implemented on this endpoint - EventNumber eventNumber; + for (auto endpoint : EnabledEndpointsWithServerCluster(WiFiNetworkDiagnostics::Id)) + { + // If Wi-Fi Network Diagnostics cluster is implemented on this endpoint + EventNumber eventNumber; - if (CHIP_NO_ERROR != LogEvent(event, endpoint, eventNumber)) - { - ChipLogError(Zcl, "WiFiDiagnosticsDelegate: Failed to record AssociationFailure event"); - } + if (CHIP_NO_ERROR != LogEvent(event, endpoint, eventNumber)) + { + ChipLogError(Zcl, "WiFiDiagnosticsDelegate: Failed to record AssociationFailure event"); } } +} + +void WiFiDiagnosticsServer::OnConnectionStatusChanged(uint8_t connectionStatus) +{ + MATTER_TRACE_SCOPE("OnConnectionStatusChanged", "WiFiDiagnosticsDelegate"); + ChipLogProgress(Zcl, "WiFiDiagnosticsDelegate: OnConnectionStatusChanged"); - // Gets when the Node’s connection status to a Wi-Fi network has changed. - void OnConnectionStatusChanged(uint8_t connectionStatus) override + Events::ConnectionStatus::Type event{ static_cast(connectionStatus) }; + for (auto endpoint : EnabledEndpointsWithServerCluster(WiFiNetworkDiagnostics::Id)) { - MATTER_TRACE_SCOPE("OnConnectionStatusChanged", "WiFiDiagnosticsDelegate"); - ChipLogProgress(Zcl, "WiFiDiagnosticsDelegate: OnConnectionStatusChanged"); + // If Wi-Fi Network Diagnostics cluster is implemented on this endpoint + EventNumber eventNumber; - Events::ConnectionStatus::Type event{ static_cast(connectionStatus) }; - for (auto endpoint : EnabledEndpointsWithServerCluster(WiFiNetworkDiagnostics::Id)) + if (CHIP_NO_ERROR != LogEvent(event, endpoint, eventNumber)) { - // If Wi-Fi Network Diagnostics cluster is implemented on this endpoint - EventNumber eventNumber; - - if (CHIP_NO_ERROR != LogEvent(event, endpoint, eventNumber)) - { - ChipLogError(Zcl, "WiFiDiagnosticsDelegate: Failed to record ConnectionStatus event"); - } + ChipLogError(Zcl, "WiFiDiagnosticsDelegate: Failed to record ConnectionStatus event"); } } -}; - -WiFiDiagnosticsDelegate gDiagnosticDelegate; +} -} // anonymous namespace +} // namespace Clusters +} // namespace app +} // namespace chip bool emberAfWiFiNetworkDiagnosticsClusterResetCountsCallback(app::CommandHandler * commandObj, const app::ConcreteCommandPath & commandPath, @@ -318,5 +329,5 @@ bool emberAfWiFiNetworkDiagnosticsClusterResetCountsCallback(app::CommandHandler void MatterWiFiNetworkDiagnosticsPluginServerInitCallback() { AttributeAccessInterfaceRegistry::Instance().Register(&gAttrAccess); - GetDiagnosticDataProvider().SetWiFiDiagnosticsDelegate(&gDiagnosticDelegate); + GetDiagnosticDataProvider().SetWiFiDiagnosticsDelegate(&WiFiDiagnosticsServer::Instance()); } diff --git a/src/app/clusters/wifi-network-diagnostics-server/wifi-network-diagnostics-server.h b/src/app/clusters/wifi-network-diagnostics-server/wifi-network-diagnostics-server.h new file mode 100644 index 00000000000000..b84d1735b2210a --- /dev/null +++ b/src/app/clusters/wifi-network-diagnostics-server/wifi-network-diagnostics-server.h @@ -0,0 +1,50 @@ +/* + * + * 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. + */ + +#pragma once + +#include + +namespace chip { +namespace app { +namespace Clusters { + +/** + * @brief wifi-diagnostics-server class + */ +class WiFiDiagnosticsServer : public DeviceLayer::WiFiDiagnosticsDelegate +{ +public: + static WiFiDiagnosticsServer & Instance(); + + // Gets called when the Node detects Node’s Wi-Fi connection has been disconnected. + void OnDisconnectionDetected(uint16_t reasonCode) override; + + // Gets called when the Node fails to associate or authenticate an access point. + void OnAssociationFailureDetected(uint8_t associationFailureCause, uint16_t status) override; + + // Gets when the Node’s connection status to a Wi-Fi network has changed. + void OnConnectionStatusChanged(uint8_t connectionStatus) override; + +private: + static WiFiDiagnosticsServer instance; +}; + +} // namespace Clusters +} // namespace app +} // namespace chip diff --git a/src/python_testing/TC_DGWIFI_2_2.py b/src/python_testing/TC_DGWIFI_2_2.py new file mode 100644 index 00000000000000..fdb3d498e307ca --- /dev/null +++ b/src/python_testing/TC_DGWIFI_2_2.py @@ -0,0 +1,219 @@ +# +# 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. +# + +# 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 +# --enable-key 000102030405060708090a0b0c0d0e0f +# script-args: > +# --storage-path admin_storage.json +# --commissioning-method on-network +# --discriminator 1234 +# --passcode 20202021 +# --hex-arg enableKey:000102030405060708090a0b0c0d0e0f +# --trace-to json:${TRACE_TEST_JSON}.json +# --trace-to perfetto:${TRACE_TEST_PERFETTO}.perfetto +# --enable-key 000102030405060708090a0b0c0d0e0f +# factory-reset: true +# quiet: true +# === END CI TEST ARGUMENTS === +# + +import asyncio + +import chip.clusters as Clusters +from chip.testing.matter_testing import MatterBaseTest, TestStep, async_test_body, default_matter_test_main +from mobly import asserts + + +class TC_DGWIFI_2_2(MatterBaseTest): + + @staticmethod + def is_valid_disconnection_event_data(event_data): + """ + Check for Disconnection event data. Verify 'reasonCode' field is int16u. + """ + return hasattr(event_data, "reasonCode") and (0 <= event_data.reasonCode <= 0xFFFF) + + @staticmethod + def is_valid_association_failure_data(event_data): + # Check for the `associationFailureCause` attribute and if its value + # is within the range of the defined enum (0..3). + cause_valid = ( + hasattr(event_data, "associationFailureCause") + and event_data.associationFailureCause in Clusters.Objects.WiFiNetworkDiagnostics.Enums.AssociationFailureCauseEnum._value2member_map_ + ) + + # Check for the `status` attribute and validate it's within the typical 802.11 range (0..65535). + # In practice, you'd compare it against known 802.11 status codes (Table 9‑50). + status_valid = ( + hasattr(event_data, "status") + and 0 <= event_data.status <= 0xFFFF + ) + + return cause_valid and status_valid + + @staticmethod + def is_valid_connection_status_data(event_data): + # Check if the `connectionStatus` attribute is present + # and if it's within the range of the defined enum. + return ( + hasattr(event_data, "connectionStatus") + and event_data.connectionStatus in Clusters.Objects.WiFiNetworkDiagnostics.Enums.ConnectionStatusEnum._value2member_map_ + ) + + # + # Methods to cause/triggers in your environment + # + async def send_wifi_disconnection_test_event_trigger(self): + # Send test event trigger to programmatically cause a Wi-Fi disconnection in the DUT. + await self.send_test_event_triggers(eventTrigger=0x0036000000000000) + + async def read_disconnection_events(self, endpoint): + event_path = [(endpoint, Clusters.WiFiNetworkDiagnostics.Events.Disconnection, 0)] + return await self.default_controller.ReadEvent( + nodeid=self.dut_node_id, events=event_path + ) + + async def send_wifi_association_failure_test_event_trigger(self): + # Send test event trigger to programmatically cause repeated association failures on the DUT. + await self.send_test_event_triggers(eventTrigger=0x0036000000000001) + + async def read_association_failure_events(self, endpoint): + event_path = [(endpoint, Clusters.WiFiNetworkDiagnostics.Events.AssociationFailure, 0)] + return await self.default_controller.ReadEvent( + nodeid=self.dut_node_id, events=event_path + ) + + async def send_wifi_reconnection_test_event_trigger(self): + # Send test event trigger to programmatically reconnect the DUT to Wi-Fi. + await self.send_test_event_triggers(eventTrigger=0x0036000000000002) + + async def read_connection_status_events(self, endpoint): + event_path = [(endpoint, Clusters.WiFiNetworkDiagnostics.Events.ConnectionStatus, 0)] + return await self.default_controller.ReadEvent( + nodeid=self.dut_node_id, events=event_path + ) + + # + # Test description & PICS + # + def desc_TC_DGWIFI_2_2(self) -> str: + """Returns a description of this test""" + return "[TC-DGWIFI-2.2] Wi-Fi Diagnostics Event Tests (Server as DUT)" + + def pics_TC_DGWIFI_2_2(self) -> list[str]: + return ["DGWIFI.S"] + + def steps_TC_DGWIFI_2_2(self) -> list[TestStep]: + steps = [ + TestStep(1, "Commissioning (already done)", is_commissioning=True), + TestStep(2, "Trigger Wi-Fi disconnection -> verify Disconnection event"), + TestStep(3, "Trigger repeated association failures -> verify AssociationFailure event"), + TestStep(4, "Reconnect Wi-Fi -> verify ConnectionStatus event"), + ] + return steps + + # --------------------------- + # Main Test Routine + # --------------------------- + @async_test_body + async def test_TC_DGWIFI_2_2(self): + endpoint = self.get_endpoint(default=0) + + # + # STEP 1: Commission DUT (already done) + # + self.step(1) + + # + # STEP 2: Cause a Wi-Fi Disconnection -> read Disconnection event from the DUT + # + self.step(2) + await self.send_wifi_disconnection_test_event_trigger() + + # Allow time for the event to propagate + await asyncio.sleep(1) + + disconnection_events = await self.read_disconnection_events(endpoint) + asserts.assert_true( + len(disconnection_events) > 0, + "No Disconnection events were received from the DUT after forced Wi-Fi disconnection." + ) + + # Validate the Disconnection event fields + for event in disconnection_events: + asserts.assert_true( + self.is_valid_disconnection_event_data(event.Data), + f"Invalid Disconnection event data: {event.Data}" + ) + + # + # STEP 3: Cause repeated association failures -> read AssociationFailure event + # + self.step(3) + await self.send_wifi_association_failure_test_event_trigger() + + # Allow time for the event + await asyncio.sleep(1) + + association_failure_events = await self.read_association_failure_events(endpoint) + asserts.assert_true( + len(association_failure_events) > 0, + "No AssociationFailure events were received from the DUT after triggering association failures." + ) + + # Validate the AssociationFailure event fields + for event in association_failure_events: + asserts.assert_true( + self.is_valid_association_failure_data(event.Data), + f"Invalid AssociationFailure event data: {event.Data}" + ) + + # + # STEP 4: Reconnect Wi-Fi -> read ConnectionStatus event + # + self.step(4) + await self.send_wifi_reconnection_test_event_trigger() + + # Allow time for the event + await asyncio.sleep(1) + + connection_status_events = await self.read_connection_status_events(endpoint) + asserts.assert_true( + len(connection_status_events) > 0, + "No ConnectionStatus events were received from the DUT after reconnecting Wi-Fi." + ) + + # Validate the ConnectionStatus event fields + for event in connection_status_events: + asserts.assert_true( + self.is_valid_connection_status_data(event.Data), + f"Invalid ConnectionStatus event data: {event.Data}" + ) + + +if __name__ == "__main__": + default_matter_test_main()