Line data Source code
1 : /*
2 : * Copyright (c) 2025 Project CHIP Authors
3 : *
4 : * Licensed under the Apache License, Version 2.0 (the "License");
5 : * you may not use this file except in compliance with the License.
6 : * You may obtain a copy of the License at
7 : *
8 : * http://www.apache.org/licenses/LICENSE-2.0
9 : *
10 : * Unless required by applicable law or agreed to in writing, software
11 : * distributed under the License is distributed on an "AS IS" BASIS,
12 : * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 : * See the License for the specific language governing permissions and
14 : * limitations under the License.
15 : */
16 : #pragma once
17 :
18 : #include <app/CommandHandler.h>
19 : #include <app/ConcreteCommandPath.h>
20 : #include <app/MessageDef/CommandDataIB.h>
21 : #include <app/data-model-provider/ActionReturnStatus.h>
22 : #include <app/data-model-provider/OperationTypes.h>
23 : #include <app/data-model/NullObject.h>
24 : #include <lib/core/CHIPError.h>
25 : #include <lib/core/DataModelTypes.h>
26 : #include <lib/core/TLVReader.h>
27 : #include <lib/core/TLVWriter.h>
28 : #include <lib/support/CodeUtils.h>
29 : #include <messaging/ExchangeContext.h>
30 :
31 : #include <memory>
32 : #include <vector>
33 :
34 : namespace chip {
35 : namespace Testing {
36 :
37 : constexpr FabricIndex kTestFabricIndex = static_cast<FabricIndex>(151);
38 :
39 : // Mock class that simulates CommandHandler behavior for unit testing, allowing capture and
40 : // verification of responses and statuses without real network interactions.
41 : class MockCommandHandler : public app::CommandHandler
42 : {
43 : public:
44 : struct ResponseRecord
45 : {
46 : CommandId commandId;
47 : System::PacketBufferHandle encodedData;
48 : app::ConcreteCommandPath path;
49 : };
50 :
51 : struct StatusRecord
52 : {
53 : app::ConcreteCommandPath path;
54 : Protocols::InteractionModel::ClusterStatusCode status;
55 : const char * context;
56 : };
57 :
58 187 : MockCommandHandler() = default;
59 : // Copying is disallowed because ResponseRecord contains PacketBufferHandle,
60 : // which is move-only and represents unique ownership of encoded TLV data.
61 : //
62 : // Move is allowed because this mock does NOT maintain any self-referencing,
63 : // intrusive, or address-stable state (unlike the real CommandHandler / Handle
64 : // system), so relocating the object is safe.
65 : MockCommandHandler(const MockCommandHandler &) = delete;
66 : MockCommandHandler & operator=(const MockCommandHandler &) = delete;
67 :
68 : MockCommandHandler(MockCommandHandler &&) = default;
69 : MockCommandHandler & operator=(MockCommandHandler &&) = default;
70 :
71 187 : ~MockCommandHandler() override = default;
72 :
73 : CHIP_ERROR FallibleAddStatus(const app::ConcreteCommandPath & aRequestCommandPath,
74 : const Protocols::InteractionModel::ClusterStatusCode & aStatus,
75 : const char * context = nullptr) override;
76 :
77 : void AddStatus(const app::ConcreteCommandPath & aRequestCommandPath,
78 : const Protocols::InteractionModel::ClusterStatusCode & aStatus, const char * context = nullptr) override;
79 :
80 453 : FabricIndex GetAccessingFabricIndex() const override { return mFabricIndex; }
81 :
82 : // Encodes and stores response data, returning error if encoding fails (fallible version for robust test handling).
83 : CHIP_ERROR AddResponseData(const app::ConcreteCommandPath & aRequestCommandPath, CommandId aResponseCommandId,
84 : const app::DataModel::EncodableToTLV & aEncodable) override;
85 :
86 : // Encodes and stores response data, without error return (non-fallible version that assumes successful encoding in tests).
87 : void AddResponse(const app::ConcreteCommandPath & aRequestCommandPath, CommandId aResponseCommandId,
88 : const app::DataModel::EncodableToTLV & aEncodable) override;
89 :
90 0 : bool IsTimedInvoke() const override { return false; }
91 0 : void FlushAcksRightAwayOnSlowCommand() override {}
92 0 : Access::SubjectDescriptor GetSubjectDescriptor() const override { return Access::SubjectDescriptor{}; }
93 0 : Messaging::ExchangeContext * GetExchangeContext() const override { return nullptr; }
94 :
95 : // Helper methods to extract response data
96 280 : bool HasResponse() const { return !mResponses.empty(); }
97 2 : size_t GetResponseCount() const { return mResponses.size(); }
98 :
99 : // Methods for working with single response (first in array)
100 : CommandId GetResponseCommandId() const { return mResponses.empty() ? 0 : mResponses[0].commandId; }
101 : const ResponseRecord & GetResponse() const { return mResponses[0]; }
102 :
103 : // Methods for working with all responses
104 : const std::vector<ResponseRecord> & GetResponses() const { return mResponses; }
105 : const ResponseRecord & GetResponse(size_t index) const { return mResponses[index]; }
106 185 : void ClearResponses() { mResponses.clear(); }
107 :
108 : // Helper methods to access stored statuses
109 0 : bool HasStatus() const { return !mStatuses.empty(); }
110 : const std::vector<StatusRecord> & GetStatuses() const { return mStatuses; }
111 0 : const StatusRecord & GetLastStatus() const { return mStatuses.back(); }
112 185 : void ClearStatuses() { mStatuses.clear(); }
113 :
114 : // Get a TLV reader positioned at the response data fields (first response)
115 : CHIP_ERROR GetResponseReader(TLV::TLVReader & reader) const;
116 :
117 : // Get a TLV reader positioned at the response data fields (specific response)
118 : CHIP_ERROR GetResponseReader(TLV::TLVReader & reader, size_t index) const;
119 :
120 : // Decode response into a specific DecodableType (first response)
121 : template <typename ResponseType>
122 141 : CHIP_ERROR DecodeResponse(ResponseType & response) const
123 : {
124 141 : TLV::TLVReader reader;
125 141 : ReturnErrorOnFailure(GetResponseReader(reader));
126 141 : return response.Decode(reader);
127 : }
128 :
129 : // Decode specific response into a specific DecodableType
130 : template <typename ResponseType>
131 : CHIP_ERROR DecodeResponse(ResponseType & response, size_t index) const
132 : {
133 : TLV::TLVReader reader;
134 : ReturnErrorOnFailure(GetResponseReader(reader, index));
135 : return response.Decode(reader);
136 : }
137 :
138 : // Configuration methods
139 73 : void SetFabricIndex(FabricIndex index) { mFabricIndex = index; }
140 :
141 : private:
142 : std::vector<ResponseRecord> mResponses;
143 : std::vector<StatusRecord> mStatuses;
144 : FabricIndex mFabricIndex = kTestFabricIndex; // Default to a clearly test-only fabric index.
145 : };
146 :
147 : } // namespace Testing
148 : } // namespace chip
|