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-provider/tests/TestConstants.h>
24 : #include <app/data-model/NullObject.h>
25 : #include <lib/core/CHIPError.h>
26 : #include <lib/core/DataModelTypes.h>
27 : #include <lib/core/TLVReader.h>
28 : #include <lib/core/TLVWriter.h>
29 : #include <lib/support/CodeUtils.h>
30 : #include <messaging/ExchangeContext.h>
31 :
32 : #include <memory>
33 : #include <vector>
34 :
35 : namespace chip {
36 : namespace Testing {
37 :
38 : // Mock class that simulates CommandHandler behavior for unit testing, allowing capture and
39 : // verification of responses and statuses without real network interactions.
40 : class MockCommandHandler : public app::CommandHandler
41 : {
42 : public:
43 : struct ResponseRecord
44 : {
45 : CommandId commandId;
46 : System::PacketBufferHandle encodedData;
47 : app::ConcreteCommandPath path;
48 : };
49 :
50 : struct StatusRecord
51 : {
52 : app::ConcreteCommandPath path;
53 : Protocols::InteractionModel::ClusterStatusCode status;
54 : const char * context;
55 : };
56 :
57 570 : MockCommandHandler() = default;
58 : // Copying is disallowed because ResponseRecord contains PacketBufferHandle,
59 : // which is move-only and represents unique ownership of encoded TLV data.
60 : //
61 : // Move is allowed because this mock does NOT maintain any self-referencing,
62 : // intrusive, or address-stable state (unlike the real CommandHandler / Handle
63 : // system), so relocating the object is safe.
64 : MockCommandHandler(const MockCommandHandler &) = delete;
65 : MockCommandHandler & operator=(const MockCommandHandler &) = delete;
66 :
67 : MockCommandHandler(MockCommandHandler &&) = default;
68 : MockCommandHandler & operator=(MockCommandHandler &&) = default;
69 :
70 570 : ~MockCommandHandler() override = default;
71 :
72 : CHIP_ERROR FallibleAddStatus(const app::ConcreteCommandPath & aRequestCommandPath,
73 : const Protocols::InteractionModel::ClusterStatusCode & aStatus,
74 : const char * context = nullptr) override;
75 :
76 : void AddStatus(const app::ConcreteCommandPath & aRequestCommandPath,
77 : const Protocols::InteractionModel::ClusterStatusCode & aStatus, const char * context = nullptr) override;
78 :
79 390 : FabricIndex GetAccessingFabricIndex() const override { return mSubjectDescriptor.fabricIndex; }
80 :
81 : // Encodes and stores response data, returning error if encoding fails (fallible version for robust test handling).
82 : CHIP_ERROR AddResponseData(const app::ConcreteCommandPath & aRequestCommandPath, CommandId aResponseCommandId,
83 : const app::DataModel::EncodableToTLV & aEncodable) override;
84 :
85 : // Encodes and stores response data, without error return (non-fallible version that assumes successful encoding in tests).
86 : void AddResponse(const app::ConcreteCommandPath & aRequestCommandPath, CommandId aResponseCommandId,
87 : const app::DataModel::EncodableToTLV & aEncodable) override;
88 :
89 0 : bool IsTimedInvoke() const override { return false; }
90 0 : void FlushAcksRightAwayOnSlowCommand() override {}
91 1330 : Access::SubjectDescriptor GetSubjectDescriptor() const override { return mSubjectDescriptor; }
92 0 : Messaging::ExchangeContext * GetExchangeContext() const override { return nullptr; }
93 :
94 : // Helper methods to extract response data
95 472 : bool HasResponse() const { return !mResponses.empty(); }
96 2 : size_t GetResponseCount() const { return mResponses.size(); }
97 :
98 : // Methods for working with single response (first in array)
99 : CommandId GetResponseCommandId() const { return mResponses.empty() ? 0 : mResponses[0].commandId; }
100 : const ResponseRecord & GetResponse() const { return mResponses[0]; }
101 :
102 : // Methods for working with all responses
103 : const std::vector<ResponseRecord> & GetResponses() const { return mResponses; }
104 : const ResponseRecord & GetResponse(size_t index) const { return mResponses[index]; }
105 503 : void ClearResponses() { mResponses.clear(); }
106 :
107 : // Helper methods to access stored statuses
108 51 : bool HasStatus() const { return !mStatuses.empty(); }
109 : const std::vector<StatusRecord> & GetStatuses() const { return mStatuses; }
110 0 : const StatusRecord & GetLastStatus() const { return mStatuses.back(); }
111 503 : void ClearStatuses() { mStatuses.clear(); }
112 :
113 : // Get a TLV reader positioned at the response data fields (first response)
114 : CHIP_ERROR GetResponseReader(TLV::TLVReader & reader) const;
115 :
116 : // Get a TLV reader positioned at the response data fields (specific response)
117 : CHIP_ERROR GetResponseReader(TLV::TLVReader & reader, size_t index) const;
118 :
119 : // Decode response into a specific DecodableType (first response)
120 : template <typename ResponseType>
121 254 : CHIP_ERROR DecodeResponse(ResponseType & response) const
122 : {
123 254 : TLV::TLVReader reader;
124 254 : ReturnErrorOnFailure(GetResponseReader(reader));
125 254 : return response.Decode(reader);
126 : }
127 :
128 : // Decode specific response into a specific DecodableType
129 : template <typename ResponseType>
130 : CHIP_ERROR DecodeResponse(ResponseType & response, size_t index) const
131 : {
132 : TLV::TLVReader reader;
133 : ReturnErrorOnFailure(GetResponseReader(reader, index));
134 : return response.Decode(reader);
135 : }
136 :
137 : // Configuration methods
138 216 : void SetFabricIndex(FabricIndex fabricIndex) { mSubjectDescriptor.fabricIndex = fabricIndex; }
139 : void SetSubjectDescriptor(const Access::SubjectDescriptor & subjectDescriptor) { mSubjectDescriptor = subjectDescriptor; }
140 :
141 : private:
142 : std::vector<ResponseRecord> mResponses;
143 : std::vector<StatusRecord> mStatuses;
144 : Access::SubjectDescriptor mSubjectDescriptor = kAdminSubjectDescriptor; // Default to a clearly test-only subject descriptor.
145 : };
146 :
147 : } // namespace Testing
148 : } // namespace chip
|