diff --git a/src/app/clusters/test-cluster-server/test-cluster-server.cpp b/src/app/clusters/test-cluster-server/test-cluster-server.cpp index de2306d0161aa6..5281598cf27309 100644 --- a/src/app/clusters/test-cluster-server/test-cluster-server.cpp +++ b/src/app/clusters/test-cluster-server/test-cluster-server.cpp @@ -54,9 +54,12 @@ class OctetStringData { public: uint8_t * Data() { return mDataBuf; } + const uint8_t * Data() const { return mDataBuf; } size_t Length() const { return mDataLen; } void SetLength(size_t size) { mDataLen = size; } + ByteSpan AsSpan() const { return ByteSpan(Data(), Length()); } + private: uint8_t mDataBuf[kAttributeEntryLength]; size_t mDataLen = 0; @@ -93,6 +96,7 @@ uint8_t gListUint8Data[kAttributeListLength]; OctetStringData gListOctetStringData[kAttributeListLength]; OctetStringData gListOperationalCert[kAttributeListLength]; Structs::TestListStructOctet::Type listStructOctetStringData[kAttributeListLength]; +OctetStringData gStructAttributeByteSpanData; Structs::SimpleStruct::Type gStructAttributeValue; NullableStruct::TypeInfo::Type gNullableStructAttributeValue; @@ -220,8 +224,7 @@ CHIP_ERROR TestAttrAccess::ReadListOctetStringAttribute(AttributeValueEncoder & return aEncoder.EncodeList([](const auto & encoder) -> CHIP_ERROR { for (uint8_t index = 0; index < kAttributeListLength; index++) { - ByteSpan span(gListOctetStringData[index].Data(), gListOctetStringData[index].Length()); - ReturnErrorOnFailure(encoder.Encode(span)); + ReturnErrorOnFailure(encoder.Encode(gListOctetStringData[index].AsSpan())); } return CHIP_NO_ERROR; }); @@ -326,9 +329,8 @@ CHIP_ERROR TestAttrAccess::WriteListStructOctetStringAttribute(AttributeValueDec memcpy(gListOperationalCert[index].Data(), entry.operationalCert.data(), entry.operationalCert.size()); gListOperationalCert[index].SetLength(entry.operationalCert.size()); - listStructOctetStringData[index].fabricIndex = entry.fabricIndex; - listStructOctetStringData[index].operationalCert = - ByteSpan(gListOperationalCert[index].Data(), gListOperationalCert[index].Length()); + listStructOctetStringData[index].fabricIndex = entry.fabricIndex; + listStructOctetStringData[index].operationalCert = gListOperationalCert[index].AsSpan(); index++; } @@ -406,7 +408,19 @@ CHIP_ERROR TestAttrAccess::ReadStructAttribute(AttributeValueEncoder & aEncoder) CHIP_ERROR TestAttrAccess::WriteStructAttribute(AttributeValueDecoder & aDecoder) { - return aDecoder.Decode(gStructAttributeValue); + // We don't support a nonempty charspan here for now. + Structs::SimpleStruct::DecodableType temp; + ReturnErrorOnFailure(aDecoder.Decode(temp)); + + VerifyOrReturnError(temp.e.empty(), CHIP_ERROR_BUFFER_TOO_SMALL); + const size_t octet_size = temp.d.size(); + VerifyOrReturnError(octet_size <= kAttributeEntryLength, CHIP_ERROR_BUFFER_TOO_SMALL); + + gStructAttributeValue = temp; + memcpy(gStructAttributeByteSpanData.Data(), temp.d.data(), octet_size); + gStructAttributeByteSpanData.SetLength(octet_size); + gStructAttributeValue.d = gStructAttributeByteSpanData.AsSpan(); + return CHIP_NO_ERROR; } CHIP_ERROR TestAttrAccess::ReadListFabricScopedAttribute(AttributeValueEncoder & aEncoder) @@ -445,29 +459,11 @@ bool emberAfTestClusterClusterTestCallback(app::CommandHandler *, const app::Con } gSimpleEnumCount = 0; - gStructAttributeValue.a = 0; - gStructAttributeValue.b = false; - gStructAttributeValue.c = SimpleEnum::kValueA; - gStructAttributeValue.d = ByteSpan(); - gStructAttributeValue.e = CharSpan(); - gStructAttributeValue.f = BitFlags(); - gStructAttributeValue.g = 0; - gStructAttributeValue.h = 0; + gStructAttributeValue = Structs::SimpleStruct::Type(); gNullableStructAttributeValue.SetNull(); - gNullablesAndOptionalsStruct.nullableInt.SetNull(); - gNullablesAndOptionalsStruct.optionalInt = NullOptional; - gNullablesAndOptionalsStruct.nullableOptionalInt = NullOptional; - gNullablesAndOptionalsStruct.nullableString.SetNull(); - gNullablesAndOptionalsStruct.optionalString = NullOptional; - gNullablesAndOptionalsStruct.nullableOptionalString = NullOptional; - gNullablesAndOptionalsStruct.nullableStruct.SetNull(); - gNullablesAndOptionalsStruct.optionalStruct = NullOptional; - gNullablesAndOptionalsStruct.nullableOptionalStruct = NullOptional; - gNullablesAndOptionalsStruct.nullableList.SetNull(); - gNullablesAndOptionalsStruct.optionalList = NullOptional; - gNullablesAndOptionalsStruct.nullableOptionalList = NullOptional; + gNullablesAndOptionalsStruct = Structs::NullablesAndOptionalsStruct::Type(); emberAfSendImmediateDefaultResponse(EMBER_ZCL_STATUS_SUCCESS); return true; diff --git a/src/app/tests/suites/TestClusterComplexTypes.yaml b/src/app/tests/suites/TestClusterComplexTypes.yaml index 3cde68b2812b95..ef3d09ab30b17c 100644 --- a/src/app/tests/suites/TestClusterComplexTypes.yaml +++ b/src/app/tests/suites/TestClusterComplexTypes.yaml @@ -197,3 +197,36 @@ tests: attribute: "boolean" arguments: value: false + + # Struct-typed attribute + - label: "Write struct-typed attribute" + command: "writeAttribute" + attribute: "struct_attr" + arguments: + value: + { + a: 5, + b: true, + c: 2, + d: "abc", + e: "", + f: 17, + g: 1.5, + h: 3.14159265358979, + } + + - label: "Read struct-typed attribute" + command: "readAttribute" + attribute: "struct_attr" + response: + value: + { + a: 5, + b: true, + c: 2, + d: "abc", + e: "", + f: 17, + g: 1.5, + h: 3.14159265358979, + } diff --git a/zzz_generated/chip-tool/zap-generated/test/Commands.h b/zzz_generated/chip-tool/zap-generated/test/Commands.h index a965113fac8426..57c30eca89bb22 100644 --- a/zzz_generated/chip-tool/zap-generated/test/Commands.h +++ b/zzz_generated/chip-tool/zap-generated/test/Commands.h @@ -59859,6 +59859,14 @@ class TestClusterComplexTypes : public TestCommand ChipLogProgress(chipTool, " ***** Test Step 20 : Write attribute that does not need timed write reset to default\n"); err = TestWriteAttributeThatDoesNotNeedTimedWriteResetToDefault_20(); break; + case 21: + ChipLogProgress(chipTool, " ***** Test Step 21 : Write struct-typed attribute\n"); + err = TestWriteStructTypedAttribute_21(); + break; + case 22: + ChipLogProgress(chipTool, " ***** Test Step 22 : Read struct-typed attribute\n"); + err = TestReadStructTypedAttribute_22(); + break; } if (CHIP_NO_ERROR != err) @@ -59870,7 +59878,7 @@ class TestClusterComplexTypes : public TestCommand private: std::atomic_uint16_t mTestIndex; - const uint16_t mTestCount = 21; + const uint16_t mTestCount = 23; chip::Optional mCluster; chip::Optional mEndpoint; @@ -59994,6 +60002,24 @@ class TestClusterComplexTypes : public TestCommand static void OnSuccessCallback_20(void * context) { (static_cast(context))->OnSuccessResponse_20(); } + static void OnFailureCallback_21(void * context, EmberAfStatus status) + { + (static_cast(context))->OnFailureResponse_21(status); + } + + static void OnSuccessCallback_21(void * context) { (static_cast(context))->OnSuccessResponse_21(); } + + static void OnFailureCallback_22(void * context, EmberAfStatus status) + { + (static_cast(context))->OnFailureResponse_22(status); + } + + static void OnSuccessCallback_22(void * context, + const chip::app::Clusters::TestCluster::Structs::SimpleStruct::DecodableType & structAttr) + { + (static_cast(context))->OnSuccessResponse_22(structAttr); + } + // // Tests methods // @@ -60483,6 +60509,59 @@ class TestClusterComplexTypes : public TestCommand void OnFailureResponse_20(EmberAfStatus status) { ThrowFailureResponse(); } void OnSuccessResponse_20() { NextTest(); } + + CHIP_ERROR TestWriteStructTypedAttribute_21() + { + const chip::EndpointId endpoint = mEndpoint.HasValue() ? mEndpoint.Value() : 1; + chip::Controller::TestClusterClusterTest cluster; + cluster.Associate(mDevices[kIdentityAlpha], endpoint); + + chip::app::Clusters::TestCluster::Structs::SimpleStruct::Type structAttrArgument; + + structAttrArgument.a = 5; + structAttrArgument.b = true; + structAttrArgument.c = static_cast(2); + structAttrArgument.d = chip::ByteSpan(chip::Uint8::from_const_char("abcgarbage: not in length on purpose"), 3); + structAttrArgument.e = chip::Span("garbage: not in length on purpose", 0); + structAttrArgument.f = static_cast>(17); + structAttrArgument.g = 1.5f; + structAttrArgument.h = 3.14159265358979; + + ReturnErrorOnFailure(cluster.WriteAttribute( + structAttrArgument, this, OnSuccessCallback_21, OnFailureCallback_21)); + return CHIP_NO_ERROR; + } + + void OnFailureResponse_21(EmberAfStatus status) { ThrowFailureResponse(); } + + void OnSuccessResponse_21() { NextTest(); } + + CHIP_ERROR TestReadStructTypedAttribute_22() + { + const chip::EndpointId endpoint = mEndpoint.HasValue() ? mEndpoint.Value() : 1; + chip::Controller::TestClusterClusterTest cluster; + cluster.Associate(mDevices[kIdentityAlpha], endpoint); + + ReturnErrorOnFailure(cluster.ReadAttribute( + this, OnSuccessCallback_22, OnFailureCallback_22)); + return CHIP_NO_ERROR; + } + + void OnFailureResponse_22(EmberAfStatus status) { ThrowFailureResponse(); } + + void OnSuccessResponse_22(const chip::app::Clusters::TestCluster::Structs::SimpleStruct::DecodableType & structAttr) + { + VerifyOrReturn(CheckValue("structAttr.a", structAttr.a, 5)); + VerifyOrReturn(CheckValue("structAttr.b", structAttr.b, true)); + VerifyOrReturn(CheckValue("structAttr.c", structAttr.c, 2)); + VerifyOrReturn(CheckValueAsString("structAttr.d", structAttr.d, chip::ByteSpan(chip::Uint8::from_const_char("abc"), 3))); + VerifyOrReturn(CheckValueAsString("structAttr.e", structAttr.e, chip::CharSpan("", 0))); + VerifyOrReturn(CheckValue("structAttr.f", structAttr.f, 17)); + VerifyOrReturn(CheckValue("structAttr.g", structAttr.g, 1.5f)); + VerifyOrReturn(CheckValue("structAttr.h", structAttr.h, 3.14159265358979)); + + NextTest(); + } }; class TestConstraints : public TestCommand