Skip to content

Commit 44bb573

Browse files
authored
Implement python test for TC_DGSW_2_2 (project-chip#36900)
* Implement python test for TC_DGSW_2_2 * Address review comment * Implement software diagnostics event trigger * Enable TestEventTrigger on the example app * Remove unused imports
1 parent 98baa6a commit 44bb573

File tree

7 files changed

+281
-0
lines changed

7 files changed

+281
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
/*
2+
*
3+
* Copyright (c) 2024 Project CHIP Authors
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
#include <app/clusters/software-diagnostics-server/SoftwareDiagnosticsTestEventTriggerHandler.h>
19+
#include <app/clusters/software-diagnostics-server/software-diagnostics-server.h>
20+
#include <platform/CHIPDeviceLayer.h>
21+
#include <platform/DiagnosticDataProvider.h>
22+
23+
using namespace chip;
24+
using namespace chip::app;
25+
using namespace chip::DeviceLayer;
26+
27+
namespace {
28+
29+
void SetTestEventTrigger_SoftwareFaultOccurred()
30+
{
31+
Clusters::SoftwareDiagnostics::Events::SoftwareFault::Type softwareFault;
32+
char threadName[kMaxThreadNameLength + 1];
33+
34+
softwareFault.id = static_cast<uint64_t>(getpid());
35+
Platform::CopyString(threadName, std::to_string(softwareFault.id).c_str());
36+
37+
softwareFault.name.SetValue(CharSpan::fromCharString(threadName));
38+
39+
std::time_t result = std::time(nullptr);
40+
// Using size of 50 as it is double the expected 25 characters "Www Mmm dd hh:mm:ss yyyy\n".
41+
char timeChar[50];
42+
if (std::strftime(timeChar, sizeof(timeChar), "%c", std::localtime(&result)))
43+
{
44+
softwareFault.faultRecording.SetValue(ByteSpan(Uint8::from_const_char(timeChar), strlen(timeChar)));
45+
}
46+
47+
Clusters::SoftwareDiagnosticsServer::Instance().OnSoftwareFaultDetect(softwareFault);
48+
}
49+
50+
} // namespace
51+
52+
bool HandleSoftwareDiagnosticsTestEventTrigger(uint64_t eventTrigger)
53+
{
54+
SoftwareDiagnosticsTrigger trigger = static_cast<SoftwareDiagnosticsTrigger>(eventTrigger);
55+
56+
switch (trigger)
57+
{
58+
case SoftwareDiagnosticsTrigger::kSoftwareFault:
59+
ChipLogProgress(Support, "[Software-Diagnostics-Test-Event] => Software Fault occurred");
60+
SetTestEventTrigger_SoftwareFaultOccurred();
61+
break;
62+
default:
63+
64+
return false;
65+
}
66+
67+
return true;
68+
}

examples/all-clusters-app/linux/BUILD.gn

+1
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ source_set("chip-all-clusters-common") {
5656
"${chip_root}/examples/all-clusters-app/all-clusters-common/src/rvc-modes.cpp",
5757
"${chip_root}/examples/all-clusters-app/all-clusters-common/src/rvc-operational-state-delegate-impl.cpp",
5858
"${chip_root}/examples/all-clusters-app/all-clusters-common/src/smco-stub.cpp",
59+
"${chip_root}/examples/all-clusters-app/all-clusters-common/src/software-diagnostics-stub.cpp",
5960
"${chip_root}/examples/all-clusters-app/all-clusters-common/src/static-supported-modes-manager.cpp",
6061
"${chip_root}/examples/all-clusters-app/all-clusters-common/src/static-supported-temperature-levels.cpp",
6162
"${chip_root}/examples/all-clusters-app/all-clusters-common/src/tcc-mode.cpp",

examples/all-clusters-app/linux/args.gni

+1
Original file line numberDiff line numberDiff line change
@@ -30,3 +30,4 @@ matter_log_json_payload_hex = true
3030
chip_enable_smoke_co_trigger = true
3131
chip_enable_boolean_state_configuration_trigger = true
3232
chip_enable_water_heater_management_trigger = true
33+
chip_enable_software_diagnostics_trigger = true

examples/platform/linux/AppMain.cpp

+7
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,9 @@
7070
#include <TracingCommandLineArgument.h> // nogncheck
7171
#endif
7272

73+
#if CHIP_DEVICE_CONFIG_ENABLE_SOFTWARE_DIAGNOSTIC_TRIGGER
74+
#include <app/clusters/software-diagnostics-server/SoftwareDiagnosticsTestEventTriggerHandler.h>
75+
#endif
7376
#if CHIP_DEVICE_CONFIG_ENABLE_OTA_REQUESTOR
7477
#include <app/clusters/ota-requestor/OTATestEventTriggerHandler.h>
7578
#endif
@@ -585,6 +588,10 @@ void ChipLinuxAppMainLoop(AppMainLoopImplementation * impl)
585588
CHIP_NO_ERROR);
586589
VerifyOrDie(sTestEventTriggerDelegate.AddHandler(&sTestEventTriggerHandler) == CHIP_NO_ERROR);
587590

591+
#if CHIP_DEVICE_CONFIG_ENABLE_SOFTWARE_DIAGNOSTIC_TRIGGER
592+
static SoftwareDiagnosticsTestEventTriggerHandler sSoftwareDiagnosticsTestEventTriggerHandler;
593+
sTestEventTriggerDelegate.AddHandler(&sSoftwareDiagnosticsTestEventTriggerHandler);
594+
#endif
588595
#if CHIP_DEVICE_CONFIG_ENABLE_OTA_REQUESTOR
589596
// We want to allow triggering OTA queries if OTA requestor is enabled
590597
static OTATestEventTriggerHandler sOtaTestEventTriggerHandler;

examples/platform/linux/BUILD.gn

+7
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ if (current_os != "nuttx") {
2525
}
2626

2727
declare_args() {
28+
chip_enable_software_diagnostics_trigger = false
2829
chip_enable_smoke_co_trigger = false
2930
chip_enable_boolean_state_configuration_trigger = false
3031
chip_enable_energy_evse_trigger = false
@@ -43,6 +44,10 @@ source_set("ota-test-event-trigger") {
4344
]
4445
}
4546

47+
source_set("software-diagnostics-test-event-trigger") {
48+
sources = [ "${chip_root}/src/app/clusters/software-diagnostics-server/SoftwareDiagnosticsTestEventTriggerHandler.h" ]
49+
}
50+
4651
source_set("smco-test-event-trigger") {
4752
sources = [ "${chip_root}/src/app/clusters/smoke-co-alarm-server/SmokeCOTestEventTriggerHandler.h" ]
4853
}
@@ -93,6 +98,7 @@ source_set("app-main") {
9398
":energy-evse-test-event-trigger",
9499
":energy-reporting-test-event-trigger",
95100
":smco-test-event-trigger",
101+
":software-diagnostics-test-event-trigger",
96102
":water-heater-management-test-event-trigger",
97103
"${chip_root}/src/data-model-providers/codegen:instance-header",
98104
"${chip_root}/src/lib",
@@ -138,6 +144,7 @@ source_set("app-main") {
138144
}
139145

140146
defines += [
147+
"CHIP_DEVICE_CONFIG_ENABLE_SOFTWARE_DIAGNOSTIC_TRIGGER=${chip_enable_software_diagnostics_trigger}",
141148
"CHIP_DEVICE_CONFIG_ENABLE_SMOKE_CO_TRIGGER=${chip_enable_smoke_co_trigger}",
142149
"CHIP_DEVICE_CONFIG_ENABLE_BOOLEAN_STATE_CONFIGURATION_TRIGGER=${chip_enable_boolean_state_configuration_trigger}",
143150
"CHIP_DEVICE_CONFIG_ENABLE_ENERGY_EVSE_TRIGGER=${chip_enable_energy_evse_trigger}",
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
/*
2+
*
3+
* Copyright (c) 2024 Project CHIP Authors
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
#pragma once
19+
20+
#include <app-common/zap-generated/cluster-objects.h>
21+
#include <app/TestEventTriggerDelegate.h>
22+
23+
/**
24+
* @brief User handler for handling the test event trigger
25+
*
26+
* @note If TestEventTrigger is enabled, it needs to be implemented in the app
27+
*
28+
* @param eventTrigger Event trigger to handle
29+
*
30+
* @retval true on success
31+
* @retval false if error happened
32+
*/
33+
bool HandleSoftwareDiagnosticsTestEventTrigger(uint64_t eventTrigger);
34+
35+
namespace chip {
36+
37+
/*
38+
* These Test EventTrigger values are specified in the TC_DGSW test plan
39+
* and are defined conditions used in test events.
40+
*
41+
* They are sent along with the enableKey (manufacturer defined secret)
42+
* in the General Diagnostic cluster TestEventTrigger command
43+
*/
44+
enum class SoftwareDiagnosticsTrigger : uint64_t
45+
{
46+
// Software Fault Test Event | Simulate the last software fault that has taken place on the Node
47+
kSoftwareFault = 0x0034000000000000,
48+
};
49+
50+
class SoftwareDiagnosticsTestEventTriggerHandler : public TestEventTriggerHandler
51+
{
52+
public:
53+
explicit SoftwareDiagnosticsTestEventTriggerHandler() {}
54+
55+
/** This function must return True if the eventTrigger is recognised and handled
56+
* It must return False to allow a higher level TestEvent handler to check other
57+
* clusters that may handle it.
58+
*/
59+
CHIP_ERROR HandleEventTrigger(uint64_t eventTrigger) override
60+
{
61+
if (HandleSoftwareDiagnosticsTestEventTrigger(eventTrigger))
62+
{
63+
return CHIP_NO_ERROR;
64+
}
65+
return CHIP_ERROR_INVALID_ARGUMENT;
66+
}
67+
};
68+
69+
} // namespace chip

src/python_testing/TC_DGSW_2_2.py

+128
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
#
2+
# Copyright (c) 2024 Project CHIP Authors
3+
# All rights reserved.
4+
#
5+
# Licensed under the Apache License, Version 2.0 (the "License");
6+
# you may not use this file except in compliance with the License.
7+
# You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing, software
12+
# distributed under the License is distributed on an "AS IS" BASIS,
13+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
# See the License for the specific language governing permissions and
15+
# limitations under the License.
16+
#
17+
18+
# See https://github.com/project-chip/connectedhomeip/blob/master/docs/testing/python.md#defining-the-ci-test-arguments
19+
# for details about the block below.
20+
#
21+
# === BEGIN CI TEST ARGUMENTS ===
22+
# test-runner-runs:
23+
# run1:
24+
# app: ${ALL_CLUSTERS_APP}
25+
# app-args: >
26+
# --discriminator 1234
27+
# --KVS kvs1
28+
# --trace-to json:${TRACE_APP}.json
29+
# --enable-key 000102030405060708090a0b0c0d0e0f
30+
# script-args: >
31+
# --storage-path admin_storage.json
32+
# --commissioning-method on-network
33+
# --discriminator 1234
34+
# --passcode 20202021
35+
# --hex-arg enableKey:000102030405060708090a0b0c0d0e0f
36+
# --trace-to json:${TRACE_TEST_JSON}.json
37+
# --trace-to perfetto:${TRACE_TEST_PERFETTO}.perfetto
38+
# --enable-key 000102030405060708090a0b0c0d0e0f
39+
# factory-reset: true
40+
# quiet: true
41+
# === END CI TEST ARGUMENTS ===
42+
#
43+
44+
import asyncio
45+
46+
import chip.clusters as Clusters
47+
from chip.testing.matter_testing import MatterBaseTest, TestStep, async_test_body, default_matter_test_main
48+
from mobly import asserts
49+
50+
51+
class TC_DGSW_2_2(MatterBaseTest):
52+
53+
@staticmethod
54+
def is_valid_uint64_value(value):
55+
return isinstance(value, int) and 0 <= value <= 0xFFFFFFFFFFFFFFFF
56+
57+
@staticmethod
58+
def is_valid_octet_string(value):
59+
return isinstance(value, (bytes, bytearray))
60+
61+
async def send_software_fault_test_event_trigger(self):
62+
await self.send_test_event_triggers(eventTrigger=0x0034000000000000)
63+
64+
async def read_software_fault_events(self, endpoint):
65+
event_path = [(endpoint, Clusters.SoftwareDiagnostics.Events.SoftwareFault, 1)]
66+
events = await self.default_controller.ReadEvent(nodeid=self.dut_node_id, events=event_path)
67+
return events
68+
69+
def desc_TC_DGSW_2_2(self) -> str:
70+
"""Returns a description of this test"""
71+
return "[TC-DGSW-2.2] Attributes with Server as DUT"
72+
73+
def pics_TC_DGSW_2_2(self) -> list[str]:
74+
return ["DGSW.S"]
75+
76+
def steps_TC_DGSW_2_2(self) -> list[TestStep]:
77+
steps = [
78+
TestStep(1, "Commissioning, already done", is_commissioning=True),
79+
TestStep(2, "Read the SoftwareFault event(s) from the DUT"),
80+
]
81+
return steps
82+
83+
@async_test_body
84+
async def test_TC_DGSW_2_2(self):
85+
86+
endpoint = self.get_endpoint(default=0)
87+
88+
# STEP 1: Commission DUT (already done)
89+
self.step(1)
90+
91+
# STEP 2: DUT sends an event report to TH. TH reads a list of SoftwareFault structs from DUT.
92+
self.step(2)
93+
94+
# Trigger a SoftwareFault event on the DUT
95+
await self.send_software_fault_test_event_trigger()
96+
97+
# Allow some time for the event to be processed
98+
await asyncio.sleep(1)
99+
100+
# Read the SoftwareFault events
101+
software_fault_events = await self.read_software_fault_events(endpoint)
102+
103+
# There should be at least one SoftwareFault event for this test to be valid.
104+
asserts.assert_true(len(software_fault_events) > 0, "No SoftwareFault events received from the DUT.")
105+
106+
# For each event, verify the data type requirements
107+
for event_data in software_fault_events:
108+
# According to the test plan and specification:
109+
# - Id is mandatory, uint64
110+
# - Name is vendor-specific string
111+
# - FaultRecording is vendor-specific payload in octstr format
112+
113+
# Validate Id
114+
asserts.assert_true(self.is_valid_uint64_value(event_data.Data.id),
115+
"Id field should be a uint64 type")
116+
117+
# Validate Name (string) - assuming event_data.Name is a string
118+
asserts.assert_true(isinstance(event_data.Data.name, str),
119+
"Name field should be a string type")
120+
121+
# Validate FaultRecording (octet_string)
122+
# Assuming event_data.FaultRecording is bytes or bytearray
123+
asserts.assert_true(self.is_valid_octet_string(event_data.Data.faultRecording),
124+
"FaultRecording field should be an octet string (bytes/bytearray)")
125+
126+
127+
if __name__ == "__main__":
128+
default_matter_test_main()

0 commit comments

Comments
 (0)