Matter SDK Coverage Report
Current view: top level - lib/support - TestPersistentStorageDelegate.h (source / functions) Coverage Total Hit
Test: SHA:4d2388ac7eed75b2fe5e05e20de377999c632502 Lines: 91.8 % 85 78
Test Date: 2025-07-27 07:17:09 Functions: 100.0 % 16 16

            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
        

Generated by: LCOV version 2.0-1