/* * 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 #include #include #include #include #include #include #include #include namespace chip { namespace app { namespace Testing { /// Contains information about a single parsed item inside an attribute data IB struct DecodedAttributeData { chip::DataVersion dataVersion; chip::app::ConcreteDataAttributePath attributePath; chip::TLV::TLVReader dataReader; CHIP_ERROR DecodeFrom(const chip::app::AttributeDataIB::Parser & parser); }; /// Maintains an internal TLV buffer for data encoding and /// decoding for ReportIBs. /// /// Main use case is that explicit TLV layouts (structure and container starting) need to be /// prepared to have a proper AttributeReportIBs::Builder/parser to exist. class EncodedReportIBs { public: /// Initialize the report structures required to encode a CHIP_ERROR StartEncoding(app::AttributeReportIBs::Builder & builder); CHIP_ERROR FinishEncoding(app::AttributeReportIBs::Builder & builder); /// Decode the embedded attribute report IBs. /// The TLVReaders inside data have a lifetime tied to the current object (its readers point /// inside the current object) CHIP_ERROR Decode(std::vector & decoded_items) const; private: constexpr static size_t kMaxTLVBufferSize = 1024; uint8_t mTlvDataBuffer[kMaxTLVBufferSize]; TLV::TLVType mOuterStructureType; TLV::TLVWriter mEncodeWriter; ByteSpan mDecodeSpan; }; /// Contains a `ReadAttributeRequest` as well as classes to convert this into a AttributeReportIBs /// and later decode it /// /// It wraps boilerplate code to obtain a `AttributeValueEncoder` as well as later decoding /// the underlying encoded data for verification. /// /// Usage: /// /// ReadOperation operation(1 /* endpoint */, 2 /* cluster */, 3 /* attribute */); /// /// auto encoder = operation.StartEncoding(/* ... */); /// ASSERT_NE(encoder.get(), nullptr); /// /// // use encoder, like pass in to a WriteAttribute() call /// /// ASSERT_EQ(operation.FinishEncoding(), CHIP_NO_ERROR); /// /// // extract the written data /// std::vector items; /// ASSERT_EQ(read_request.GetEncodedIBs().Decode(items), CHIP_NO_ERROR); /// /// // can use items::dataReader for a chip::TLV::TLVReader access to the underlying data /// // for example /// uint32_t value = 0; /// chip::TLV::TLVReader reader(items[0].dataReader); /// ASSERT_EQ(reader.Get(value), CHIP_NO_ERROR); /// ASSERT_EQ(value, 123u); /// /// class ReadOperation { public: /// Represents parameters for StartEncoding class EncodingParams { public: EncodingParams() {} EncodingParams & SetDataVersion(chip::DataVersion v) { mDataVersion = v; return *this; } EncodingParams & SetIsFabricFiltered(bool filtered) { mIsFabricFiltered = filtered; return *this; } EncodingParams & SetEncodingState(const AttributeEncodeState & state) { mAttributeEncodeState = state; return *this; } chip::DataVersion GetDataVersion() const { return mDataVersion; } bool GetIsFabricFiltered() const { return mIsFabricFiltered; } const AttributeEncodeState & GetAttributeEncodeState() const { return mAttributeEncodeState; } private: chip::DataVersion mDataVersion = 0x1234; bool mIsFabricFiltered = false; AttributeEncodeState mAttributeEncodeState; }; ReadOperation(const ConcreteAttributePath & path) { mRequest.path = path; mRequest.subjectDescriptor = kDenySubjectDescriptor; } ReadOperation(EndpointId endpoint, ClusterId cluster, AttributeId attribute) : ReadOperation(ConcreteAttributePath(endpoint, cluster, attribute)) {} ReadOperation & SetSubjectDescriptor(const chip::Access::SubjectDescriptor & descriptor) { VerifyOrDie(mState == State::kInitializing); mRequest.subjectDescriptor = descriptor; return *this; } ReadOperation & SetReadFlags(const BitFlags & flags) { VerifyOrDie(mState == State::kInitializing); mRequest.readFlags = flags; return *this; } ReadOperation & SetOperationFlags(const BitFlags & flags) { VerifyOrDie(mState == State::kInitializing); mRequest.operationFlags = flags; return *this; } ReadOperation & SetPathExpanded(bool value) { VerifyOrDie(mState == State::kInitializing); mRequest.path.mExpanded = value; return *this; } /// Start the encoding of a new element with the given data version associated to it. /// /// The input attribute encoding state will be attached to the returned value encoded (so that /// encoding for list elements is possible) /// std::unique_ptr StartEncoding(const EncodingParams & params = EncodingParams()); /// Completes the encoding and finalizes the undelying AttributeReport. /// /// Call this to finish a set of `StartEncoding` values and have access to /// the underlying `GetEncodedIBs` CHIP_ERROR FinishEncoding(); /// Get the underlying read request (i.e. path and flags) for this request const DataModel::ReadAttributeRequest & GetRequest() const { return mRequest; } /// Once encoding has finished, you can get access to the underlying /// written data via GetEncodedIBs. const EncodedReportIBs & GetEncodedIBs() const { VerifyOrDie(mState == State::kFinished); return mEncodedIBs; } private: enum class State { kInitializing, // Setting up initial values (i.e. setting up mRequest) kEncoding, // Encoding values via StartEncoding kFinished, // FinishEncoding has been called }; State mState = State::kInitializing; DataModel::ReadAttributeRequest mRequest; // encoded-used classes EncodedReportIBs mEncodedIBs; AttributeReportIBs::Builder mAttributeReportIBsBuilder; }; } // namespace Testing } // namespace app } // namespace chip