Line data Source code
1 : /*
2 : *
3 : * Copyright (c) 2021-2022 Project CHIP Authors
4 : * All rights reserved.
5 : *
6 : * Licensed under the Apache License, Version 2.0 (the "License");
7 : * you may not use this file except in compliance with the License.
8 : * You may obtain a copy of the License at
9 : *
10 : * http://www.apache.org/licenses/LICENSE-2.0
11 : *
12 : * Unless required by applicable law or agreed to in writing, software
13 : * distributed under the License is distributed on an "AS IS" BASIS,
14 : * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 : * See the License for the specific language governing permissions and
16 : * limitations under the License.
17 : */
18 :
19 : #pragma once
20 :
21 : #include <algorithm>
22 : #include <lib/core/CHIPCore.h>
23 : #include <lib/core/CHIPPersistentStorageDelegate.h>
24 : #include <lib/support/DLLUtil.h>
25 : #include <lib/support/SafeInt.h>
26 : #include <lib/support/logging/CHIPLogging.h>
27 : #include <map>
28 : #include <set>
29 : #include <string>
30 : #include <vector>
31 :
32 : namespace chip {
33 :
34 : /**
35 : * Implementation of PersistentStorageDelegate suitable for unit tests,
36 : * where persistence lasts for the object's lifetime and where all data is retained
37 : * is memory.
38 : *
39 : * This version also has "poison keys" which, if accessed, yield an error. This can
40 : * be used in unit tests to make sure a module making use of the PersistentStorageDelegate
41 : * does not access some particular keys which should remain untouched by underlying
42 : * logic.
43 : */
44 : class TestPersistentStorageDelegate : public PersistentStorageDelegate
45 : {
46 : public:
47 : enum class LoggingLevel : unsigned
48 : {
49 : kDisabled = 0,
50 : kLogMutation = 1,
51 : kLogMutationAndReads = 2,
52 : };
53 :
54 640 : TestPersistentStorageDelegate() {}
55 :
56 14547 : CHIP_ERROR SyncGetKeyValue(const char * key, void * buffer, uint16_t & size) override
57 : {
58 14547 : if (mLoggingLevel >= LoggingLevel::kLogMutationAndReads)
59 : {
60 23 : ChipLogDetail(Test, "TestPersistentStorageDelegate::SyncGetKeyValue: Get key '%s'", StringOrNullMarker(key));
61 : }
62 :
63 14547 : CHIP_ERROR err = SyncGetKeyValueInternal(key, buffer, size);
64 :
65 14547 : if (mLoggingLevel >= LoggingLevel::kLogMutationAndReads)
66 : {
67 23 : if (err == CHIP_ERROR_PERSISTED_STORAGE_VALUE_NOT_FOUND)
68 : {
69 14 : ChipLogDetail(Test, "--> TestPersistentStorageDelegate::SyncGetKeyValue: Key '%s' not found",
70 : StringOrNullMarker(key));
71 : }
72 9 : else if (err == CHIP_ERROR_PERSISTED_STORAGE_FAILED)
73 : {
74 0 : ChipLogDetail(Test, "--> TestPersistentStorageDelegate::SyncGetKeyValue: Key '%s' is a poison key",
75 : StringOrNullMarker(key));
76 : }
77 : }
78 :
79 14547 : return err;
80 : }
81 :
82 9585 : CHIP_ERROR SyncSetKeyValue(const char * key, const void * value, uint16_t size) override
83 : {
84 9585 : if (mLoggingLevel >= LoggingLevel::kLogMutation)
85 : {
86 26 : ChipLogDetail(Test, "TestPersistentStorageDelegate::SyncSetKeyValue, Set key '%s' with data size %u", key,
87 : static_cast<unsigned>(size));
88 : }
89 :
90 9585 : CHIP_ERROR err = SyncSetKeyValueInternal(key, value, size);
91 :
92 9585 : if (mLoggingLevel >= LoggingLevel::kLogMutationAndReads)
93 : {
94 12 : if (err == CHIP_ERROR_PERSISTED_STORAGE_FAILED)
95 : {
96 0 : ChipLogDetail(Test, "--> TestPersistentStorageDelegate::SyncSetKeyValue: Key '%s' is a poison key",
97 : StringOrNullMarker(key));
98 : }
99 : }
100 :
101 9585 : return err;
102 : }
103 :
104 1566 : CHIP_ERROR SyncDeleteKeyValue(const char * key) override
105 : {
106 1566 : if (mLoggingLevel >= LoggingLevel::kLogMutation)
107 : {
108 7 : ChipLogDetail(Test, "TestPersistentStorageDelegate::SyncDeleteKeyValue, Delete key '%s'", StringOrNullMarker(key));
109 : }
110 1566 : CHIP_ERROR err = SyncDeleteKeyValueInternal(key);
111 :
112 1566 : if (mLoggingLevel >= LoggingLevel::kLogMutation)
113 : {
114 7 : if (err == CHIP_ERROR_PERSISTED_STORAGE_VALUE_NOT_FOUND)
115 : {
116 0 : ChipLogDetail(Test, "--> TestPersistentStorageDelegate::SyncDeleteKeyValue: Key '%s' not found",
117 : StringOrNullMarker(key));
118 : }
119 7 : else if (err == CHIP_ERROR_PERSISTED_STORAGE_FAILED)
120 : {
121 0 : ChipLogDetail(Test, "--> TestPersistentStorageDelegate::SyncDeleteKeyValue: Key '%s' is a poison key",
122 : StringOrNullMarker(key));
123 : }
124 : }
125 :
126 1566 : return err;
127 : }
128 :
129 : /**
130 : * @brief Adds a "poison key": a key that, if read/written, implies some bad
131 : * behavior occurred.
132 : *
133 : * @param key - Poison key to add to the set.
134 : */
135 4 : virtual void AddPoisonKey(const std::string & key) { mPoisonKeys.insert(key); }
136 :
137 : /**
138 : * Allows subsequent writes to be rejected for unit testing purposes.
139 : */
140 2 : virtual void SetRejectWrites(bool rejectWrites) { mRejectWrites = rejectWrites; }
141 :
142 : /**
143 : * @brief Clear all "poison keys"
144 : *
145 : */
146 3 : virtual void ClearPoisonKeys() { mPoisonKeys.clear(); }
147 :
148 : /**
149 : * @brief Reset entire contents back to empty. This does NOT clear the "poison keys"
150 : *
151 : */
152 404 : virtual void ClearStorage() { mStorage.clear(); }
153 :
154 : /**
155 : * @return the number of keys currently written in storage
156 : */
157 109 : virtual size_t GetNumKeys() { return mStorage.size(); }
158 :
159 : /**
160 : * @return a set of all the keys stored
161 : */
162 2 : virtual std::set<std::string> GetKeys()
163 : {
164 2 : std::set<std::string> keys;
165 :
166 14 : for (auto it = mStorage.begin(); it != mStorage.end(); ++it)
167 : {
168 12 : keys.insert(it->first);
169 : }
170 :
171 2 : return keys;
172 : }
173 :
174 : /**
175 : * @brief Determine if storage has a given key
176 : *
177 : * @param key - key to find (case-sensitive)
178 : * @return true if key is present in storage, false otherwise
179 : */
180 16115 : virtual bool HasKey(const std::string & key) { return (mStorage.find(key) != mStorage.end()); }
181 :
182 : /**
183 : * @brief Set the logging verbosity for debugging
184 : *
185 : * @param loggingLevel - logging verbosity level to set
186 : */
187 2 : virtual void SetLoggingLevel(LoggingLevel loggingLevel) { mLoggingLevel = loggingLevel; }
188 :
189 : /**
190 : * @brief Dump a list of all storage keys, sorted alphabetically
191 : */
192 1 : virtual void DumpKeys()
193 : {
194 1 : ChipLogError(Test, "TestPersistentStorageDelegate::DumpKeys: %u keys", static_cast<unsigned>(GetNumKeys()));
195 :
196 1 : auto allKeys = GetKeys();
197 1 : std::vector<std::string> allKeysSorted(allKeys.cbegin(), allKeys.cend());
198 1 : std::sort(allKeysSorted.begin(), allKeysSorted.end());
199 :
200 11 : for (const std::string & key : allKeysSorted)
201 : {
202 10 : (void) key.c_str(); // Guard against log level disabling error logging which would make `key` unused.
203 10 : ChipLogError(Test, " -> %s", key.c_str());
204 : }
205 1 : }
206 :
207 : protected:
208 14547 : virtual CHIP_ERROR SyncGetKeyValueInternal(const char * key, void * buffer, uint16_t & size)
209 : {
210 14547 : VerifyOrReturnError((buffer != nullptr) || (size == 0), CHIP_ERROR_INVALID_ARGUMENT);
211 :
212 : // Making sure poison keys are not accessed
213 14545 : if (mPoisonKeys.find(std::string(key)) != mPoisonKeys.end())
214 : {
215 0 : return CHIP_ERROR_PERSISTED_STORAGE_FAILED;
216 : }
217 :
218 14545 : bool contains = HasKey(key);
219 14545 : VerifyOrReturnError(contains, CHIP_ERROR_PERSISTED_STORAGE_VALUE_NOT_FOUND);
220 :
221 5694 : std::vector<uint8_t> & value = mStorage[key];
222 5694 : size_t valueSize = value.size();
223 5694 : if (!CanCastTo<uint16_t>(valueSize))
224 : {
225 0 : return CHIP_ERROR_PERSISTED_STORAGE_FAILED;
226 : }
227 :
228 5694 : uint16_t valueSizeUint16 = static_cast<uint16_t>(valueSize);
229 5694 : VerifyOrReturnError(size != 0 || valueSizeUint16 != 0, CHIP_NO_ERROR);
230 5689 : VerifyOrReturnError(buffer != nullptr, CHIP_ERROR_BUFFER_TOO_SMALL);
231 :
232 5587 : uint16_t sizeToCopy = std::min(size, valueSizeUint16);
233 :
234 5587 : size = sizeToCopy;
235 5587 : memcpy(buffer, value.data(), size);
236 5587 : return size < valueSizeUint16 ? CHIP_ERROR_BUFFER_TOO_SMALL : CHIP_NO_ERROR;
237 : }
238 :
239 9585 : virtual CHIP_ERROR SyncSetKeyValueInternal(const char * key, const void * value, uint16_t size)
240 : {
241 : // Make sure writes are allowed and poison keys are not accessed
242 9585 : if (mRejectWrites || mPoisonKeys.find(std::string(key)) != mPoisonKeys.end())
243 : {
244 6 : return CHIP_ERROR_PERSISTED_STORAGE_FAILED;
245 : }
246 :
247 : // Handle empty values
248 9579 : if (value == nullptr)
249 : {
250 2 : if (size == 0)
251 : {
252 1 : mStorage[key] = std::vector<uint8_t>();
253 1 : return CHIP_NO_ERROR;
254 : }
255 :
256 1 : return CHIP_ERROR_INVALID_ARGUMENT;
257 : }
258 : // Handle non-empty values
259 :
260 9577 : const uint8_t * bytes = static_cast<const uint8_t *>(value);
261 9577 : mStorage[key] = std::vector<uint8_t>(bytes, bytes + size);
262 9577 : return CHIP_NO_ERROR;
263 : }
264 :
265 1566 : virtual CHIP_ERROR SyncDeleteKeyValueInternal(const char * key)
266 : {
267 : // Make sure writes are allowed and poison keys are not accessed
268 1566 : if (mRejectWrites || mPoisonKeys.find(std::string(key)) != mPoisonKeys.end())
269 : {
270 0 : return CHIP_ERROR_PERSISTED_STORAGE_FAILED;
271 : }
272 :
273 1566 : bool contains = HasKey(key);
274 1566 : VerifyOrReturnError(contains, CHIP_ERROR_PERSISTED_STORAGE_VALUE_NOT_FOUND);
275 1410 : mStorage.erase(key);
276 1410 : return CHIP_NO_ERROR;
277 : }
278 :
279 : std::map<std::string, std::vector<uint8_t>> mStorage;
280 : std::set<std::string> mPoisonKeys;
281 : bool mRejectWrites = false;
282 : LoggingLevel mLoggingLevel = LoggingLevel::kDisabled;
283 : };
284 :
285 : } // namespace chip
|