From c77fb77e13c01d8198e22e4cb08a7cf1bf6d2c7b Mon Sep 17 00:00:00 2001 From: Nikita Poltorapavlo Date: Fri, 2 Feb 2024 17:50:50 +0200 Subject: [PATCH] test --- .github/workflows/L0-PersistentStore-grpc.yml | 53 ++ ...istentStore.yml => L0-PersistentStore.yml} | 28 +- ...lite.yml => L1-PersistentStore-sqlite.yml} | 20 +- PersistentStore/CMakeLists.txt | 24 +- PersistentStore/Module.h | 7 + PersistentStore/PersistentStore.cpp | 78 +-- PersistentStore/PersistentStore.h | 5 + PersistentStore/PersistentStoreJsonRpc.cpp | 175 ++--- PersistentStore/grpc/Store2.cpp | 10 + PersistentStore/grpc/Store2.h | 250 +++++++ PersistentStore/grpc/l0test/CMakeLists.txt | 46 ++ .../grpc/l0test/SecureStorageServiceMock.h | 14 + PersistentStore/grpc/l0test/Server.h | 20 + PersistentStore/grpc/l0test/Store2Test.cpp | 170 +++++ PersistentStore/grpc/secure_storage.cmake | 67 ++ PersistentStore/grpc/secure_storage.proto | 136 ++++ PersistentStore/iarm/TimeSync.cpp | 10 + PersistentStore/iarm/TimeSync.h | 103 +++ .../{l1test => l0test}/CMakeLists.txt | 22 +- .../l0test/PersistentStoreTest.cpp | 656 ++++++++++++++++++ .../{l1test => l0test}/ServiceMock.h | 12 +- PersistentStore/l0test/Store2Mock.h | 18 + PersistentStore/l0test/StoreCacheMock.h | 13 + PersistentStore/l0test/StoreInspectorMock.h | 15 + PersistentStore/l0test/StoreLimitMock.h | 14 + PersistentStore/l1test/JsonRpcTest.cpp | 265 ------- PersistentStore/l1test/PluginTest.cpp | 121 ---- PersistentStore/l1test/StoreTest.cpp | 149 ---- PersistentStore/sqlite/Handle.cpp | 184 ----- PersistentStore/sqlite/Handle.h | 60 -- PersistentStore/sqlite/Store2.cpp | 10 + PersistentStore/sqlite/Store2.h | 321 +++++++++ PersistentStore/sqlite/Store2Type.h | 288 -------- .../sqlite/Store2WithClockSyncType.h | 134 ---- .../sqlite/Store2WithReconnectType.h | 64 -- PersistentStore/sqlite/StoreCache.cpp | 10 + PersistentStore/sqlite/StoreCache.h | 114 +++ PersistentStore/sqlite/StoreCacheType.h | 72 -- PersistentStore/sqlite/StoreInspector.cpp | 10 + PersistentStore/sqlite/StoreInspector.h | 190 +++++ PersistentStore/sqlite/StoreInspectorType.h | 161 ----- PersistentStore/sqlite/StoreLimit.cpp | 10 + PersistentStore/sqlite/StoreLimit.h | 183 +++++ PersistentStore/sqlite/StoreLimitType.h | 138 ---- PersistentStore/sqlite/l1test/CMakeLists.txt | 29 +- PersistentStore/sqlite/l1test/HandleTest.cpp | 48 -- .../sqlite/l1test/Store2NotificationMock.h | 13 + PersistentStore/sqlite/l1test/Store2Test.cpp | 305 +++----- .../sqlite/l1test/Store2WithReconnectTest.cpp | 51 -- .../sqlite/l1test/StoreCacheTest.cpp | 33 +- .../sqlite/l1test/StoreInspectorTest.cpp | 127 ++-- .../sqlite/l1test/StoreLimitTest.cpp | 134 ++-- 52 files changed, 2849 insertions(+), 2341 deletions(-) create mode 100644 .github/workflows/L0-PersistentStore-grpc.yml rename .github/workflows/{L1-PersistentStore.yml => L0-PersistentStore.yml} (54%) rename .github/workflows/{L1-Sqlite.yml => L1-PersistentStore-sqlite.yml} (69%) create mode 100644 PersistentStore/grpc/Store2.cpp create mode 100644 PersistentStore/grpc/Store2.h create mode 100644 PersistentStore/grpc/l0test/CMakeLists.txt create mode 100644 PersistentStore/grpc/l0test/SecureStorageServiceMock.h create mode 100644 PersistentStore/grpc/l0test/Server.h create mode 100644 PersistentStore/grpc/l0test/Store2Test.cpp create mode 100644 PersistentStore/grpc/secure_storage.cmake create mode 100644 PersistentStore/grpc/secure_storage.proto create mode 100644 PersistentStore/iarm/TimeSync.cpp create mode 100644 PersistentStore/iarm/TimeSync.h rename PersistentStore/{l1test => l0test}/CMakeLists.txt (76%) create mode 100644 PersistentStore/l0test/PersistentStoreTest.cpp rename PersistentStore/{l1test => l0test}/ServiceMock.h (93%) create mode 100644 PersistentStore/l0test/Store2Mock.h create mode 100644 PersistentStore/l0test/StoreCacheMock.h create mode 100644 PersistentStore/l0test/StoreInspectorMock.h create mode 100644 PersistentStore/l0test/StoreLimitMock.h delete mode 100644 PersistentStore/l1test/JsonRpcTest.cpp delete mode 100644 PersistentStore/l1test/PluginTest.cpp delete mode 100644 PersistentStore/l1test/StoreTest.cpp delete mode 100644 PersistentStore/sqlite/Handle.cpp delete mode 100644 PersistentStore/sqlite/Handle.h create mode 100644 PersistentStore/sqlite/Store2.cpp create mode 100644 PersistentStore/sqlite/Store2.h delete mode 100644 PersistentStore/sqlite/Store2Type.h delete mode 100644 PersistentStore/sqlite/Store2WithClockSyncType.h delete mode 100644 PersistentStore/sqlite/Store2WithReconnectType.h create mode 100644 PersistentStore/sqlite/StoreCache.cpp create mode 100644 PersistentStore/sqlite/StoreCache.h delete mode 100644 PersistentStore/sqlite/StoreCacheType.h create mode 100644 PersistentStore/sqlite/StoreInspector.cpp create mode 100644 PersistentStore/sqlite/StoreInspector.h delete mode 100644 PersistentStore/sqlite/StoreInspectorType.h create mode 100644 PersistentStore/sqlite/StoreLimit.cpp create mode 100644 PersistentStore/sqlite/StoreLimit.h delete mode 100644 PersistentStore/sqlite/StoreLimitType.h delete mode 100644 PersistentStore/sqlite/l1test/HandleTest.cpp create mode 100644 PersistentStore/sqlite/l1test/Store2NotificationMock.h delete mode 100644 PersistentStore/sqlite/l1test/Store2WithReconnectTest.cpp diff --git a/.github/workflows/L0-PersistentStore-grpc.yml b/.github/workflows/L0-PersistentStore-grpc.yml new file mode 100644 index 0000000000..769021aa4e --- /dev/null +++ b/.github/workflows/L0-PersistentStore-grpc.yml @@ -0,0 +1,53 @@ +name: L0-PersistentStore-grpc + +on: + push: + paths: + - PersistentStore/grpc/** + pull_request: + paths: + - PersistentStore/grpc/** + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + with: + path: ${{github.repository}} + + - name: Install valgrind, coverage, cmake, protoc, grpc_cpp_plugin, grpc + run: | + sudo apt update + sudo apt install -y valgrind lcov cmake protobuf-compiler protobuf-compiler-grpc libgrpc++-dev + + - name: Build Thunder + working-directory: ${{github.workspace}} + run: sh +x ${GITHUB_REPOSITORY}/.github/workflows/BuildThunder.sh + + - name: Build + working-directory: ${{github.workspace}} + run: | + cmake -S ${GITHUB_REPOSITORY}/PersistentStore/grpc/l0test -B build/grpcl0test -DCMAKE_INSTALL_PREFIX="install/usr" -DCMAKE_CXX_FLAGS="--coverage -Wall -Werror" + cmake --build build/grpcl0test --target install + + - name: Run + working-directory: ${{github.workspace}} + run: PATH=${PWD}/install/usr/bin:${PATH} LD_LIBRARY_PATH=${PWD}/install/usr/lib:${LD_LIBRARY_PATH} valgrind --tool=memcheck --log-file=valgrind_log --leak-check=yes --show-reachable=yes --track-fds=yes --fair-sched=try grpcl0test + + - name: Generate coverage + working-directory: ${{github.workspace}} + run: | + lcov -c -o coverage.info -d build/grpcl0test + genhtml -o coverage coverage.info + + - name: Upload artifacts + if: ${{ !env.ACT }} + uses: actions/upload-artifact@v4 + with: + name: artifacts + path: | + coverage/ + valgrind_log + if-no-files-found: warn diff --git a/.github/workflows/L1-PersistentStore.yml b/.github/workflows/L0-PersistentStore.yml similarity index 54% rename from .github/workflows/L1-PersistentStore.yml rename to .github/workflows/L0-PersistentStore.yml index 5a5c391d89..1b68bf704f 100644 --- a/.github/workflows/L1-PersistentStore.yml +++ b/.github/workflows/L0-PersistentStore.yml @@ -1,4 +1,4 @@ -name: L1-PersistentStore +name: L0-PersistentStore on: push: @@ -13,43 +13,41 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: - path: rdkservices + path: ${{github.repository}} - - name: Install valgrind, coverage, cmake, sqlite + - name: Install valgrind, coverage, cmake run: | sudo apt update - sudo apt install -y valgrind lcov cmake libsqlite3-dev + sudo apt install -y valgrind lcov cmake - name: Build Thunder working-directory: ${{github.workspace}} - run: sh +x rdkservices/.github/workflows/BuildThunder.sh - - - name: Configure - working-directory: ${{github.workspace}} - run: cmake -S rdkservices/PersistentStore/l1test -B build/persistentstorel1test -DCMAKE_INSTALL_PREFIX="install/usr" -DCMAKE_CXX_FLAGS="--coverage -Wall -Werror" + run: sh +x ${GITHUB_REPOSITORY}/.github/workflows/BuildThunder.sh - name: Build working-directory: ${{github.workspace}} - run: cmake --build build/persistentstorel1test --target install + run: | + cmake -S ${GITHUB_REPOSITORY}/PersistentStore/l0test -B build/persistentstorel0test -DCMAKE_INSTALL_PREFIX="install/usr" -DCMAKE_CXX_FLAGS="--coverage -Wall -Werror" + cmake --build build/persistentstorel0test --target install - name: Run working-directory: ${{github.workspace}} - run: PATH=${PWD}/install/usr/bin:${PATH} LD_LIBRARY_PATH=${PWD}/install/usr/lib:${LD_LIBRARY_PATH} valgrind --tool=memcheck --log-file=valgrind_log --leak-check=yes --show-reachable=yes --track-fds=yes --fair-sched=try persistentstorel1test + run: PATH=${PWD}/install/usr/bin:${PATH} LD_LIBRARY_PATH=${PWD}/install/usr/lib:${LD_LIBRARY_PATH} valgrind --tool=memcheck --log-file=valgrind_log --leak-check=yes --show-reachable=yes --track-fds=yes --fair-sched=try persistentstorel0test - name: Generate coverage working-directory: ${{github.workspace}} run: | - lcov -c -o coverage.info -d build/persistentstorel1test + lcov -c -o coverage.info -d build/persistentstorel0test genhtml -o coverage coverage.info - name: Upload artifacts if: ${{ !env.ACT }} - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: artifacts path: | coverage/ valgrind_log - if-no-files-found: warn \ No newline at end of file + if-no-files-found: warn diff --git a/.github/workflows/L1-Sqlite.yml b/.github/workflows/L1-PersistentStore-sqlite.yml similarity index 69% rename from .github/workflows/L1-Sqlite.yml rename to .github/workflows/L1-PersistentStore-sqlite.yml index 12fed1aa4e..52912a79ee 100644 --- a/.github/workflows/L1-Sqlite.yml +++ b/.github/workflows/L1-PersistentStore-sqlite.yml @@ -1,4 +1,4 @@ -name: L1-Sqlite +name: L1-PersistentStore-sqlite on: push: @@ -13,9 +13,9 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: - path: rdkservices + path: ${{github.repository}} - name: Install valgrind, coverage, cmake, sqlite run: | @@ -24,15 +24,13 @@ jobs: - name: Build Thunder working-directory: ${{github.workspace}} - run: sh +x rdkservices/.github/workflows/BuildThunder.sh - - - name: Configure - working-directory: ${{github.workspace}} - run: cmake -S rdkservices/PersistentStore/sqlite/l1test -B build/sqlitel1test -DCMAKE_INSTALL_PREFIX="install/usr" -DCMAKE_CXX_FLAGS="--coverage -Wall -Werror" + run: sh +x ${GITHUB_REPOSITORY}/.github/workflows/BuildThunder.sh - name: Build working-directory: ${{github.workspace}} - run: cmake --build build/sqlitel1test --target install + run: | + cmake -S ${GITHUB_REPOSITORY}/PersistentStore/sqlite/l1test -B build/sqlitel1test -DCMAKE_INSTALL_PREFIX="install/usr" -DCMAKE_CXX_FLAGS="--coverage -Wall -Werror" + cmake --build build/sqlitel1test --target install - name: Run working-directory: ${{github.workspace}} @@ -46,10 +44,10 @@ jobs: - name: Upload artifacts if: ${{ !env.ACT }} - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: artifacts path: | coverage/ valgrind_log - if-no-files-found: warn \ No newline at end of file + if-no-files-found: warn diff --git a/PersistentStore/CMakeLists.txt b/PersistentStore/CMakeLists.txt index 848d6697e5..c2a4d8c13f 100644 --- a/PersistentStore/CMakeLists.txt +++ b/PersistentStore/CMakeLists.txt @@ -15,14 +15,13 @@ # See the License for the specific language governing permissions and # limitations under the License. -project(PersistentStore) +cmake_minimum_required(VERSION 3.14) -cmake_minimum_required(VERSION 3.3) +project(PersistentStore) +project_version(1.0.0) find_package(WPEFramework) -project_version(1.0.0) - set(MODULE_NAME ${NAMESPACE}${PROJECT_NAME}) message("Setup ${MODULE_NAME} v${PROJECT_VERSION}") @@ -46,7 +45,7 @@ find_library(IARMBUS_LIBRARIES NAMES IARMBus) if (IARMBUS_LIBRARIES) message("Building with IARMBus") - add_definitions(-DWITH_CLOCK_SYNC) + set(IARM_SOURCES iarm/TimeSync.cpp) find_path(IARMBUS_INCLUDE_DIRS NAMES libIBus.h PATH_SUFFIXES rdk/iarmbus REQUIRED) find_path(IARMSYS_INCLUDE_DIRS NAMES sysMgr.h PATH_SUFFIXES rdk/iarmmgrs/sysmgr REQUIRED) endif () @@ -54,14 +53,19 @@ endif () add_library(${MODULE_NAME} SHARED PersistentStore.cpp PersistentStoreJsonRpc.cpp - sqlite/Handle.cpp + grpc/Store2.cpp + sqlite/Store2.cpp + sqlite/StoreCache.cpp + sqlite/StoreInspector.cpp + sqlite/StoreLimit.cpp + ${IARM_SOURCES} Module.cpp - ) +) set_target_properties(${MODULE_NAME} PROPERTIES CXX_STANDARD 11 CXX_STANDARD_REQUIRED YES - ) +) add_definitions(${SQLITE_CFLAGS_OTHER}) link_directories(${SQLITE_LIBRARY_DIRS}) @@ -71,14 +75,14 @@ target_include_directories(${MODULE_NAME} PRIVATE ${SQLITE_INCLUDE_DIRS} ${IARMBUS_INCLUDE_DIRS} ${IARMSYS_INCLUDE_DIRS} - ) +) target_link_libraries(${MODULE_NAME} PRIVATE ${NAMESPACE}Plugins::${NAMESPACE}Plugins ${NAMESPACE}Definitions::${NAMESPACE}Definitions ${SQLITE_LIBRARIES} ${IARMBUS_LIBRARIES} - ) +) install(TARGETS ${MODULE_NAME} DESTINATION lib/${STORAGE_DIRECTORY}/plugins) diff --git a/PersistentStore/Module.h b/PersistentStore/Module.h index 516a52ba33..10043c5203 100644 --- a/PersistentStore/Module.h +++ b/PersistentStore/Module.h @@ -29,5 +29,12 @@ #include #endif +#define URI_ENV "PERSISTENTSTORE_URI" +#define PATH_ENV "PERSISTENTSTORE_PATH" +#define MAXSIZE_ENV "PERSISTENTSTORE_MAXSIZE" +#define MAXVALUE_ENV "PERSISTENTSTORE_MAXVALUE" +#define LIMIT_ENV "PERSISTENTSTORE_LIMIT" +#define CLOCKSYNC_ENV "PERSISTENTSTORE_CLOCKSYNC" + #undef EXTERNAL #define EXTERNAL diff --git a/PersistentStore/PersistentStore.cpp b/PersistentStore/PersistentStore.cpp index e3b92030ab..02e86615c8 100644 --- a/PersistentStore/PersistentStore.cpp +++ b/PersistentStore/PersistentStore.cpp @@ -18,17 +18,6 @@ */ #include "PersistentStore.h" - -#include "sqlite/Handle.h" -#include "sqlite/Store2Type.h" -#include "sqlite/Store2WithReconnectType.h" -#if defined(WITH_CLOCK_SYNC) -#include "sqlite/Store2WithClockSyncType.h" -#endif -#include "sqlite/StoreCacheType.h" -#include "sqlite/StoreInspectorType.h" -#include "sqlite/StoreLimitType.h" - #include #define API_VERSION_NUMBER_MAJOR 1 @@ -52,25 +41,6 @@ namespace { namespace Plugin { -#if defined(WITH_CLOCK_SYNC) - class SqliteStore2 : public Sqlite::Store2WithClockSyncType>> { - }; -#else - class SqliteStore2 : public Sqlite::Store2WithReconnectType> { - }; -#endif - class SqliteStoreCache : public Sqlite::StoreCacheType { - }; - class SqliteStoreInspector : public Sqlite::StoreInspectorType { - }; - class SqliteStoreLimit : public Sqlite::StoreLimitType { - }; - - SERVICE_REGISTRATION(SqliteStore2, 1, 0); - SERVICE_REGISTRATION(SqliteStoreCache, 1, 0); - SERVICE_REGISTRATION(SqliteStoreInspector, 1, 0); - SERVICE_REGISTRATION(SqliteStoreLimit, 1, 0); - SERVICE_REGISTRATION(PersistentStore, API_VERSION_NUMBER_MAJOR, API_VERSION_NUMBER_MINOR, API_VERSION_NUMBER_PATCH); const string PersistentStore::Initialize(PluginHost::IShell* service) @@ -82,16 +52,12 @@ namespace Plugin { auto configLine = service->ConfigLine(); _config.FromString(configLine); - ASSERT(!_config.Path.Value().empty()); - { Core::File file(_config.Path.Value()); Core::File oldFile(_config.LegacyPath.Value()); if (file.Name() != oldFile.Name()) { if (oldFile.Exists()) { if (!file.Exists()) { - // Rename will fail if the files are not on the same filesystem - // Make a copy and then remove the original file std::ifstream in(oldFile.Name(), std::ios::in | std::ios::binary); std::ofstream out(file.Name(), std::ios::out | std::ios::binary); out << in.rdbuf(); @@ -102,49 +68,69 @@ namespace Plugin { } } - Core::SystemInfo::SetEnvironment(_T("PERSISTENTSTORE_PATH"), _config.Path.Value()); - Core::SystemInfo::SetEnvironment(_T("PERSISTENTSTORE_MAXSIZE"), std::to_string(_config.MaxSize.Value())); - Core::SystemInfo::SetEnvironment(_T("PERSISTENTSTORE_MAXVALUE"), std::to_string(_config.MaxValue.Value())); - Core::SystemInfo::SetEnvironment(_T("PERSISTENTSTORE_LIMIT"), std::to_string(_config.Limit.Value())); + Core::SystemInfo::SetEnvironment(PATH_ENV, _config.Path.Value()); + Core::SystemInfo::SetEnvironment(MAXSIZE_ENV, std::to_string(_config.MaxSize.Value())); + Core::SystemInfo::SetEnvironment(MAXVALUE_ENV, std::to_string(_config.MaxValue.Value())); + Core::SystemInfo::SetEnvironment(LIMIT_ENV, std::to_string(_config.Limit.Value())); uint32_t connectionId; - Store2::ScopeMapType initList1 = { - { Exchange::IStore2::ScopeType::DEVICE, service->Root(connectionId, 2000, _T("SqliteStore2")) } - }; + Store2::ScopeMapType initList1; + auto deviceStore2 = service->Root(connectionId, 2000, _T("SqliteStore2")); + if (deviceStore2 != nullptr) { + initList1.emplace(Exchange::IStore2::ScopeType::DEVICE, deviceStore2); + } + auto accountStore2 = service->Root(connectionId, 2000, _T("GrpcStore2")); + if (accountStore2 != nullptr) { + initList1.emplace(Exchange::IStore2::ScopeType::ACCOUNT, accountStore2); + } _store2 = Core::Service::Create(initList1); + if (deviceStore2 != nullptr) { + deviceStore2->Release(); + } + if (accountStore2 != nullptr) { + accountStore2->Release(); + } _store2->Register(&_store2Sink); _store = Core::Service::Create(_store2); _storeCache = service->Root(connectionId, 2000, _T("SqliteStoreCache")); _storeInspector = service->Root(connectionId, 2000, _T("SqliteStoreInspector")); _storeLimit = service->Root(connectionId, 2000, _T("SqliteStoreLimit")); + _timeSync = service->Root(connectionId, 2000, _T("IarmTimeSync")); + if (_timeSync != nullptr) { + _timeSync->Synchronize(); + } return result; } void PersistentStore::Deinitialize(PluginHost::IShell* /* service */) { - if (_store) { + if (_store != nullptr) { _store->Release(); _store = nullptr; } - if (_store2) { + if (_store2 != nullptr) { _store2->Unregister(&_store2Sink); _store2->Release(); _store2 = nullptr; } - if (_storeCache) { + if (_storeCache != nullptr) { _storeCache->Release(); _storeCache = nullptr; } - if (_storeInspector) { + if (_storeInspector != nullptr) { _storeInspector->Release(); _storeInspector = nullptr; } - if (_storeLimit) { + if (_storeLimit != nullptr) { _storeLimit->Release(); _storeLimit = nullptr; } + if (_timeSync != nullptr) { + _timeSync->Release(); + _timeSync = nullptr; + } } string PersistentStore::Information() const diff --git a/PersistentStore/PersistentStore.h b/PersistentStore/PersistentStore.h index 1c8d3b96af..f71da0f003 100644 --- a/PersistentStore/PersistentStore.h +++ b/PersistentStore/PersistentStore.h @@ -24,6 +24,7 @@ #include #include #include +#include #include namespace WPEFramework { @@ -224,6 +225,9 @@ namespace Plugin { Store2(const ScopeMapType& map) : _scopeMap(map) { + for (auto const& x : _scopeMap) { + x.second->AddRef(); + } } ~Store2() override { @@ -343,6 +347,7 @@ namespace Plugin { Exchange::IStoreCache* _storeCache; Exchange::IStoreInspector* _storeInspector; Exchange::IStoreLimit* _storeLimit; + Exchange::ITimeSync* _timeSync; Core::Sink _store2Sink; }; diff --git a/PersistentStore/PersistentStoreJsonRpc.cpp b/PersistentStore/PersistentStoreJsonRpc.cpp index fa568808c5..faa89dd147 100644 --- a/PersistentStore/PersistentStoreJsonRpc.cpp +++ b/PersistentStore/PersistentStoreJsonRpc.cpp @@ -56,21 +56,14 @@ namespace Plugin { uint32_t PersistentStore::endpoint_setValue(const SetValueParamsData& params, DeleteKeyResultInfo& response) { - uint32_t result; - - if (!params.Namespace.IsSet() || !params.Key.IsSet() || !params.Value.IsSet()) { - // Mandatory params are not set - result = Core::ERROR_INVALID_INPUT_LENGTH; - } else { - result = _store2->SetValue( - Exchange::IStore2::ScopeType(params.Scope.Value()), - params.Namespace.Value(), - params.Key.Value(), - params.Value.Value(), - params.Ttl.Value()); - if (result == Core::ERROR_NONE) { - response.Success = true; - } + auto result = _store2->SetValue( + Exchange::IStore2::ScopeType(params.Scope.Value()), + params.Namespace.Value(), + params.Key.Value(), + params.Value.Value(), + params.Ttl.Value()); + if (result == Core::ERROR_NONE) { + response.Success = true; } return result; @@ -78,27 +71,20 @@ namespace Plugin { uint32_t PersistentStore::endpoint_getValue(const DeleteKeyParamsInfo& params, GetValueResultData& response) { - uint32_t result; - - if (!params.Namespace.IsSet() || !params.Key.IsSet()) { - // Mandatory params are not set - result = Core::ERROR_INVALID_INPUT_LENGTH; - } else { - string value; - uint32_t ttl; - result = _store2->GetValue( - Exchange::IStore2::ScopeType(params.Scope.Value()), - params.Namespace.Value(), - params.Key.Value(), - value, - ttl); - if (result == Core::ERROR_NONE) { - response.Value = value; - if (ttl > 0) { - response.Ttl = ttl; - } - response.Success = true; + string value; + uint32_t ttl; + auto result = _store2->GetValue( + Exchange::IStore2::ScopeType(params.Scope.Value()), + params.Namespace.Value(), + params.Key.Value(), + value, + ttl); + if (result == Core::ERROR_NONE) { + response.Value = value; + if (ttl > 0) { + response.Ttl = ttl; } + response.Success = true; } return result; @@ -106,19 +92,12 @@ namespace Plugin { uint32_t PersistentStore::endpoint_deleteKey(const DeleteKeyParamsInfo& params, DeleteKeyResultInfo& response) { - uint32_t result; - - if (!params.Namespace.IsSet() || !params.Key.IsSet()) { - // Mandatory params are not set - result = Core::ERROR_INVALID_INPUT_LENGTH; - } else { - result = _store2->DeleteKey( - Exchange::IStore2::ScopeType(params.Scope.Value()), - params.Namespace.Value(), - params.Key.Value()); - if (result == Core::ERROR_NONE) { - response.Success = true; - } + auto result = _store2->DeleteKey( + Exchange::IStore2::ScopeType(params.Scope.Value()), + params.Namespace.Value(), + params.Key.Value()); + if (result == Core::ERROR_NONE) { + response.Success = true; } return result; @@ -126,18 +105,11 @@ namespace Plugin { uint32_t PersistentStore::endpoint_deleteNamespace(const DeleteNamespaceParamsInfo& params, DeleteKeyResultInfo& response) { - uint32_t result; - - if (!params.Namespace.IsSet()) { - // Mandatory params are not set - result = Core::ERROR_INVALID_INPUT_LENGTH; - } else { - result = _store2->DeleteNamespace( - Exchange::IStore2::ScopeType(params.Scope.Value()), - params.Namespace.Value()); - if (result == Core::ERROR_NONE) { - response.Success = true; - } + auto result = _store2->DeleteNamespace( + Exchange::IStore2::ScopeType(params.Scope.Value()), + params.Namespace.Value()); + if (result == Core::ERROR_NONE) { + response.Success = true; } return result; @@ -145,25 +117,18 @@ namespace Plugin { uint32_t PersistentStore::endpoint_getKeys(const DeleteNamespaceParamsInfo& params, GetKeysResultData& response) { - uint32_t result; - - if (!params.Namespace.IsSet()) { - // Mandatory params are not set - result = Core::ERROR_INVALID_INPUT_LENGTH; - } else { - RPC::IStringIterator* it; - result = _storeInspector->GetKeys( - Exchange::IStore2::ScopeType(params.Scope.Value()), - params.Namespace.Value(), - it); - if (result == Core::ERROR_NONE) { - string element; - while (it->Next(element) == true) { - response.Keys.Add() = element; - } - it->Release(); - response.Success = true; + RPC::IStringIterator* it; + auto result = _storeInspector->GetKeys( + Exchange::IStore2::ScopeType(params.Scope.Value()), + params.Namespace.Value(), + it); + if (result == Core::ERROR_NONE) { + string element; + while (it->Next(element) == true) { + response.Keys.Add() = element; } + it->Release(); + response.Success = true; } return result; @@ -171,10 +136,8 @@ namespace Plugin { uint32_t PersistentStore::endpoint_getNamespaces(const GetNamespacesParamsInfo& params, GetNamespacesResultData& response) { - uint32_t result; - RPC::IStringIterator* it; - result = _storeInspector->GetNamespaces( + auto result = _storeInspector->GetNamespaces( Exchange::IStore2::ScopeType(params.Scope.Value()), it); if (result == Core::ERROR_NONE) { @@ -192,10 +155,8 @@ namespace Plugin { // Deprecated uint32_t PersistentStore::endpoint_getStorageSize(const GetNamespacesParamsInfo& params, JsonObject& response) { - uint32_t result; - Exchange::IStoreInspector::INamespaceSizeIterator* it; - result = _storeInspector->GetStorageSizes( + auto result = _storeInspector->GetStorageSizes( Exchange::IStore2::ScopeType(params.Scope.Value()), it); if (result == Core::ERROR_NONE) { @@ -214,10 +175,8 @@ namespace Plugin { uint32_t PersistentStore::endpoint_getStorageSizes(const GetNamespacesParamsInfo& params, GetStorageSizesResultData& response) { - uint32_t result; - Exchange::IStoreInspector::INamespaceSizeIterator* it; - result = _storeInspector->GetStorageSizes( + auto result = _storeInspector->GetStorageSizes( Exchange::IStore2::ScopeType(params.Scope.Value()), it); if (result == Core::ERROR_NONE) { @@ -235,9 +194,7 @@ namespace Plugin { uint32_t PersistentStore::endpoint_flushCache(DeleteKeyResultInfo& response) { - uint32_t result; - - result = _storeCache->FlushCache(); + auto result = _storeCache->FlushCache(); if (result == Core::ERROR_NONE) { response.Success = true; } @@ -247,20 +204,13 @@ namespace Plugin { uint32_t PersistentStore::endpoint_getNamespaceStorageLimit(const DeleteNamespaceParamsInfo& params, GetNamespaceStorageLimitResultData& response) { - uint32_t result; - - if (!params.Namespace.IsSet()) { - // Mandatory params are not set - result = Core::ERROR_INVALID_INPUT_LENGTH; - } else { - uint32_t size; - result = _storeLimit->GetNamespaceStorageLimit( - Exchange::IStore2::ScopeType(params.Scope.Value()), - params.Namespace.Value(), - size); - if (result == Core::ERROR_NONE) { - response.StorageLimit = size; - } + uint32_t size; + auto result = _storeLimit->GetNamespaceStorageLimit( + Exchange::IStore2::ScopeType(params.Scope.Value()), + params.Namespace.Value(), + size); + if (result == Core::ERROR_NONE) { + response.StorageLimit = size; } return result; @@ -268,19 +218,10 @@ namespace Plugin { uint32_t PersistentStore::endpoint_setNamespaceStorageLimit(const SetNamespaceStorageLimitParamsData& params) { - uint32_t result; - - if (!params.Namespace.IsSet() || !params.StorageLimit.IsSet()) { - // Mandatory params are not set - result = Core::ERROR_INVALID_INPUT_LENGTH; - } else { - result = _storeLimit->SetNamespaceStorageLimit( - Exchange::IStore2::ScopeType(params.Scope.Value()), - params.Namespace.Value(), - params.StorageLimit.Value()); - } - - return result; + return _storeLimit->SetNamespaceStorageLimit( + Exchange::IStore2::ScopeType(params.Scope.Value()), + params.Namespace.Value(), + params.StorageLimit.Value()); } } // namespace Plugin diff --git a/PersistentStore/grpc/Store2.cpp b/PersistentStore/grpc/Store2.cpp new file mode 100644 index 0000000000..e1fb6e9ed1 --- /dev/null +++ b/PersistentStore/grpc/Store2.cpp @@ -0,0 +1,10 @@ +#include "Store2.h" + +namespace WPEFramework { +namespace Plugin { + namespace Grpc { + using GrpcStore2 = Store2; + SERVICE_REGISTRATION(GrpcStore2, 1, 0); + } // namespace Grpc +} // namespace Plugin +} // namespace WPEFramework diff --git a/PersistentStore/grpc/Store2.h b/PersistentStore/grpc/Store2.h new file mode 100644 index 0000000000..dbe71388f4 --- /dev/null +++ b/PersistentStore/grpc/Store2.h @@ -0,0 +1,250 @@ +/* + * If not stated otherwise in this file or this component's LICENSE file the + * following copyright and licenses apply: + * + * Copyright 2022 RDK Management + * + * 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 "../Module.h" +#include "secure_storage.grpc.pb.h" +#include +#include + +namespace WPEFramework { +namespace Plugin { + namespace Grpc { + + class Store2 : public Exchange::IStore2 { + private: + Store2(const Store2&) = delete; + Store2& operator=(const Store2&) = delete; + + public: + Store2() + : Store2(getenv(URI_ENV)) + { + } + Store2(const string& uri) + : IStore2() + , _uri(uri) + { + Open(); + } + ~Store2() override = default; + + private: + void Open() + { + std::shared_ptr<::grpc::ChannelInterface> channel; + if ((_uri.find("localhost") == string::npos) && (_uri.find("0.0.0.0") == string::npos)) { + channel = grpc::CreateChannel( + _uri, + grpc::CompositeChannelCredentials( + grpc::SslCredentials(grpc::SslCredentialsOptions()), + grpc::AccessTokenCredentials(""))); + } else { + channel = grpc::CreateChannel(_uri, grpc::InsecureChannelCredentials()); + } + _stub = ::distp::gateway::secure_storage::v1::SecureStorageService::NewStub(channel); + } + + public: + uint32_t Register(INotification* notification) override + { + Core::SafeSyncType lock(_clientLock); + + ASSERT(std::find(_clients.begin(), _clients.end(), notification) == _clients.end()); + + notification->AddRef(); + _clients.push_back(notification); + + return Core::ERROR_NONE; + } + uint32_t Unregister(INotification* notification) override + { + Core::SafeSyncType lock(_clientLock); + + std::list::iterator + index(std::find(_clients.begin(), _clients.end(), notification)); + + ASSERT(index != _clients.end()); + + if (index != _clients.end()) { + notification->Release(); + _clients.erase(index); + } + + return Core::ERROR_NONE; + } + + uint32_t SetValue(const ScopeType scope, const string& ns, const string& key, const string& value, const uint32_t ttl) override + { + ASSERT(scope == ScopeType::ACCOUNT); + + uint32_t result; + + grpc::ClientContext context; + ::distp::gateway::secure_storage::v1::UpdateValueRequest request; + auto v = new ::distp::gateway::secure_storage::v1::Value(); + v->set_value(value); + if (ttl != 0) { + auto t = new google::protobuf::Duration(); + t->set_seconds(ttl); + v->set_allocated_ttl(t); + } + auto k = new ::distp::gateway::secure_storage::v1::Key(); + k->set_app_id(ns); + k->set_key(key); + k->set_scope(::distp::gateway::secure_storage::v1::Scope::SCOPE_ACCOUNT); + v->set_allocated_key(k); + request.set_allocated_value(v); + ::distp::gateway::secure_storage::v1::UpdateValueResponse response; + auto status = _stub->UpdateValue(&context, request, &response); + + if (status.ok()) { + OnValueChanged(ns, key, value); + result = Core::ERROR_NONE; + } else { + OnError(__FUNCTION__, status); + result = Core::ERROR_GENERAL; + } + + return result; + } + uint32_t GetValue(const ScopeType scope, const string& ns, const string& key, string& value, uint32_t& ttl) override + { + ASSERT(scope == ScopeType::ACCOUNT); + + uint32_t result; + + grpc::ClientContext context; + ::distp::gateway::secure_storage::v1::GetValueRequest request; + auto k = new ::distp::gateway::secure_storage::v1::Key(); + k->set_app_id(ns); + k->set_key(key); + k->set_scope(::distp::gateway::secure_storage::v1::Scope::SCOPE_ACCOUNT); + request.set_allocated_key(k); + ::distp::gateway::secure_storage::v1::GetValueResponse response; + auto status = _stub->GetValue(&context, request, &response); + + if (status.ok() && response.has_value()) { + auto v = response.value(); + if (v.has_ttl()) { + value = v.value(); + ttl = v.ttl().seconds(); + result = Core::ERROR_NONE; + } else if (v.has_expire_time()) { + if (getenv(CLOCKSYNC_ENV) != nullptr) { + value = v.value(); + int64_t now = Core::Time::Now().Ticks() / Core::Time::TicksPerMillisecond / 1000; + ttl = v.expire_time().seconds() - now; + result = Core::ERROR_NONE; + } else { + result = Core::ERROR_PENDING_CONDITIONS; + } + } else { + value = v.value(); + ttl = 0; + result = Core::ERROR_NONE; + } + } else { + OnError(__FUNCTION__, status); + result = Core::ERROR_GENERAL; + } + + return result; + } + uint32_t DeleteKey(const ScopeType scope, const string& ns, const string& key) override + { + ASSERT(scope == ScopeType::ACCOUNT); + + uint32_t result; + + grpc::ClientContext context; + ::distp::gateway::secure_storage::v1::DeleteValueRequest request; + auto k = new ::distp::gateway::secure_storage::v1::Key(); + k->set_app_id(ns); + k->set_key(key); + k->set_scope(::distp::gateway::secure_storage::v1::Scope::SCOPE_ACCOUNT); + request.set_allocated_key(k); + ::distp::gateway::secure_storage::v1::DeleteValueResponse response; + auto status = _stub->DeleteValue(&context, request, &response); + + if (status.ok()) { + result = Core::ERROR_NONE; + } else { + OnError(__FUNCTION__, status); + result = Core::ERROR_GENERAL; + } + + return result; + } + uint32_t DeleteNamespace(const ScopeType scope, const string& ns) override + { + ASSERT(scope == ScopeType::ACCOUNT); + + uint32_t result; + + grpc::ClientContext context; + ::distp::gateway::secure_storage::v1::DeleteAllValuesRequest request; + request.set_app_id(ns); + request.set_scope(::distp::gateway::secure_storage::v1::Scope::SCOPE_ACCOUNT); + ::distp::gateway::secure_storage::v1::DeleteAllValuesResponse response; + auto status = _stub->DeleteAllValues(&context, request, &response); + + if (status.ok()) { + result = Core::ERROR_NONE; + } else { + OnError(__FUNCTION__, status); + result = Core::ERROR_GENERAL; + } + + return result; + } + + BEGIN_INTERFACE_MAP(Store2) + INTERFACE_ENTRY(IStore2) + END_INTERFACE_MAP + + private: + void OnValueChanged(const string& ns, const string& key, const string& value) + { + Core::SafeSyncType lock(_clientLock); + + std::list::iterator + index(_clients.begin()); + + while (index != _clients.end()) { + (*index)->ValueChanged(ScopeType::DEVICE, ns, key, value); + index++; + } + } + void OnError(const char* fn, const grpc::Status& status) const + { + TRACE(Trace::Error, (_T("%s grpc error %d %s %s"), fn, status.error_code(), status.error_message().c_str(), status.error_details().c_str())); + } + + private: + const string _uri; + std::unique_ptr<::distp::gateway::secure_storage::v1::SecureStorageService::Stub> _stub; + std::list _clients; + Core::CriticalSection _clientLock; + }; + + } // namespace Grpc +} // namespace Plugin +} // namespace WPEFramework diff --git a/PersistentStore/grpc/l0test/CMakeLists.txt b/PersistentStore/grpc/l0test/CMakeLists.txt new file mode 100644 index 0000000000..351cdd15c1 --- /dev/null +++ b/PersistentStore/grpc/l0test/CMakeLists.txt @@ -0,0 +1,46 @@ +# If not stated otherwise in this file or this component's LICENSE file the +# following copyright and licenses apply: +# +# Copyright 2020 RDK Management +# +# 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. + +cmake_minimum_required(VERSION 3.14) + +project(grpcl0test) + +set(CMAKE_CXX_STANDARD 11) + +include(FetchContent) +FetchContent_Declare( + googletest + URL https://github.com/google/googletest/archive/609281088cfefc76f9d0ce82e1ff6c30cc3591e5.zip +) +FetchContent_MakeAvailable(googletest) + +find_package(WPEFramework) +find_package(${NAMESPACE}Plugins REQUIRED) + +add_executable(${PROJECT_NAME} + ../../Module.cpp + Store2Test.cpp +) + +target_link_libraries(${PROJECT_NAME} + gmock_main + ${NAMESPACE}Plugins::${NAMESPACE}Plugins +) + +include(../secure_storage.cmake) + +install(TARGETS ${PROJECT_NAME} DESTINATION bin) diff --git a/PersistentStore/grpc/l0test/SecureStorageServiceMock.h b/PersistentStore/grpc/l0test/SecureStorageServiceMock.h new file mode 100644 index 0000000000..3165e289eb --- /dev/null +++ b/PersistentStore/grpc/l0test/SecureStorageServiceMock.h @@ -0,0 +1,14 @@ +#pragma once + +#include "secure_storage.grpc.pb.h" +#include + +class SecureStorageServiceMock : public ::distp::gateway::secure_storage::v1::SecureStorageService::Service { +public: + ~SecureStorageServiceMock() override = default; + MOCK_METHOD(::grpc::Status, GetValue, (::grpc::ServerContext * context, const ::distp::gateway::secure_storage::v1::GetValueRequest* request, ::distp::gateway::secure_storage::v1::GetValueResponse* response), (override)); + MOCK_METHOD(::grpc::Status, UpdateValue, (::grpc::ServerContext * context, const ::distp::gateway::secure_storage::v1::UpdateValueRequest* request, ::distp::gateway::secure_storage::v1::UpdateValueResponse* response), (override)); + MOCK_METHOD(::grpc::Status, DeleteValue, (::grpc::ServerContext * context, const ::distp::gateway::secure_storage::v1::DeleteValueRequest* request, ::distp::gateway::secure_storage::v1::DeleteValueResponse* response), (override)); + MOCK_METHOD(::grpc::Status, DeleteAllValues, (::grpc::ServerContext * context, const ::distp::gateway::secure_storage::v1::DeleteAllValuesRequest* request, ::distp::gateway::secure_storage::v1::DeleteAllValuesResponse* response), (override)); + MOCK_METHOD(::grpc::Status, SeedValue, (::grpc::ServerContext * context, const ::distp::gateway::secure_storage::v1::SeedValueRequest* request, ::distp::gateway::secure_storage::v1::SeedValueResponse* response), (override)); +}; diff --git a/PersistentStore/grpc/l0test/Server.h b/PersistentStore/grpc/l0test/Server.h new file mode 100644 index 0000000000..b7adb02151 --- /dev/null +++ b/PersistentStore/grpc/l0test/Server.h @@ -0,0 +1,20 @@ +#pragma once + +#include +#include +#include + +struct Server { + std::unique_ptr server; + Server(const std::string& uri, grpc::Service* service) + { + grpc::ServerBuilder builder; + builder.AddListeningPort(uri, grpc::InsecureServerCredentials()); + builder.RegisterService(service); + server = builder.BuildAndStart(); + } + ~Server() + { + server->Shutdown(); + } +}; diff --git a/PersistentStore/grpc/l0test/Store2Test.cpp b/PersistentStore/grpc/l0test/Store2Test.cpp new file mode 100644 index 0000000000..65c76b53c8 --- /dev/null +++ b/PersistentStore/grpc/l0test/Store2Test.cpp @@ -0,0 +1,170 @@ +#include +#include + +#include "../Store2.h" +#include "SecureStorageServiceMock.h" +#include "Server.h" + +using ::distp::gateway::secure_storage::v1::DeleteAllValuesRequest; +using ::distp::gateway::secure_storage::v1::DeleteAllValuesResponse; +using ::distp::gateway::secure_storage::v1::DeleteValueRequest; +using ::distp::gateway::secure_storage::v1::DeleteValueResponse; +using ::distp::gateway::secure_storage::v1::GetValueRequest; +using ::distp::gateway::secure_storage::v1::GetValueResponse; +using ::distp::gateway::secure_storage::v1::Key; +using ::distp::gateway::secure_storage::v1::Scope; +using ::distp::gateway::secure_storage::v1::UpdateValueRequest; +using ::distp::gateway::secure_storage::v1::UpdateValueResponse; +using ::distp::gateway::secure_storage::v1::Value; +using ::testing::_; +using ::testing::Eq; +using ::testing::Gt; +using ::testing::Invoke; +using ::testing::IsFalse; +using ::testing::IsTrue; +using ::testing::Le; +using ::testing::NiceMock; +using ::testing::Test; +using ::WPEFramework::Core::Time; +using ::WPEFramework::Exchange::IStore2; +using ::WPEFramework::Plugin::Grpc::Store2; + +const auto kUri = "0.0.0.0:50051"; +const auto kValue = "value_1"; +const auto kKey = "key_1"; +const auto kAppId = "app_id_1"; +const auto kTtl = 100; +const auto kScope = Scope::SCOPE_ACCOUNT; + +class AStore2 : public Test { +protected: + NiceMock service; + Server server; + WPEFramework::Core::ProxyType store2; + AStore2() + : server(kUri, &service) + , store2(WPEFramework::Core::ProxyType::Create(kUri)) + { + } +}; + +TEST_F(AStore2, GetsValueWithTtl) +{ + GetValueRequest req; + ON_CALL(service, GetValue(_, _, _)) + .WillByDefault(Invoke( + [&](::grpc::ServerContext*, const GetValueRequest* request, GetValueResponse* response) { + req = (*request); + auto v = new Value(); + v->set_value(kValue); + auto t = new google::protobuf::Duration(); + t->set_seconds(kTtl); + v->set_allocated_ttl(t); + auto k = new Key(); + k->set_key(request->key().key()); + k->set_app_id(request->key().app_id()); + k->set_scope(request->key().scope()); + v->set_allocated_key(k); + response->set_allocated_value(v); + return grpc::Status::OK; + })); + + string v; + uint32_t t; + ASSERT_THAT(store2->GetValue(IStore2::ScopeType::ACCOUNT, kAppId, kKey, v, t), Eq(WPEFramework::Core::ERROR_NONE)); + ASSERT_THAT(req.has_key(), IsTrue()); + EXPECT_THAT(req.key().key(), Eq(kKey)); + EXPECT_THAT(req.key().app_id(), Eq(kAppId)); + EXPECT_THAT(req.key().scope(), Eq(kScope)); + EXPECT_THAT(v, Eq(kValue)); + EXPECT_THAT(t, Eq(kTtl)); +} + +TEST_F(AStore2, GetsValueWithExpireTime) +{ + GetValueRequest req; + ON_CALL(service, GetValue(_, _, _)) + .WillByDefault(Invoke( + [&](::grpc::ServerContext*, const GetValueRequest* request, GetValueResponse* response) { + req = (*request); + auto v = new Value(); + v->set_value(kValue); + auto t = new google::protobuf::Timestamp(); + t->set_seconds(kTtl + (Time::Now().Ticks() / Time::TicksPerMillisecond / 1000)); + v->set_allocated_expire_time(t); + auto k = new Key(); + k->set_key(request->key().key()); + k->set_app_id(request->key().app_id()); + k->set_scope(request->key().scope()); + v->set_allocated_key(k); + response->set_allocated_value(v); + return grpc::Status::OK; + })); + + string v; + uint32_t t; + ASSERT_THAT(store2->GetValue(IStore2::ScopeType::ACCOUNT, kAppId, kKey, v, t), Eq(WPEFramework::Core::ERROR_PENDING_CONDITIONS)); + WPEFramework::Core::SystemInfo::SetEnvironment(CLOCKSYNC_ENV, ""); + ASSERT_THAT(store2->GetValue(IStore2::ScopeType::ACCOUNT, kAppId, kKey, v, t), Eq(WPEFramework::Core::ERROR_NONE)); + ::unsetenv(CLOCKSYNC_ENV); + ASSERT_THAT(req.has_key(), IsTrue()); + EXPECT_THAT(req.key().key(), Eq(kKey)); + EXPECT_THAT(req.key().app_id(), Eq(kAppId)); + EXPECT_THAT(req.key().scope(), Eq(kScope)); + EXPECT_THAT(v, Eq(kValue)); + EXPECT_THAT(t, Le(kTtl)); + EXPECT_THAT(t, Gt(0)); +} + +TEST_F(AStore2, SetsValueWithTtl) +{ + UpdateValueRequest req; + ON_CALL(service, UpdateValue(_, _, _)) + .WillByDefault(Invoke( + [&](::grpc::ServerContext*, const UpdateValueRequest* request, UpdateValueResponse*) { + req = (*request); + return grpc::Status::OK; + })); + + ASSERT_THAT(store2->SetValue(IStore2::ScopeType::ACCOUNT, kAppId, kKey, kValue, kTtl), Eq(WPEFramework::Core::ERROR_NONE)); + ASSERT_THAT(req.has_value(), IsTrue()); + EXPECT_THAT(req.value().value(), Eq(kValue)); + ASSERT_THAT(req.value().has_key(), IsTrue()); + EXPECT_THAT(req.value().key().key(), Eq(kKey)); + EXPECT_THAT(req.value().key().app_id(), Eq(kAppId)); + EXPECT_THAT(req.value().key().scope(), Eq(kScope)); + ASSERT_THAT(req.value().has_ttl(), IsTrue()); + EXPECT_THAT(req.value().ttl().seconds(), Eq(kTtl)); +} + +TEST_F(AStore2, DeletesKey) +{ + DeleteValueRequest req; + ON_CALL(service, DeleteValue(_, _, _)) + .WillByDefault(Invoke( + [&](::grpc::ServerContext*, const DeleteValueRequest* request, DeleteValueResponse*) { + req = (*request); + return grpc::Status::OK; + })); + + ASSERT_THAT(store2->DeleteKey(IStore2::ScopeType::ACCOUNT, kAppId, kKey), Eq(WPEFramework::Core::ERROR_NONE)); + ASSERT_THAT(req.has_key(), IsTrue()); + EXPECT_THAT(req.key().key(), Eq(kKey)); + EXPECT_THAT(req.key().app_id(), Eq(kAppId)); + EXPECT_THAT(req.key().scope(), Eq(kScope)); +} + +TEST_F(AStore2, DeletesNamespace) +{ + DeleteAllValuesRequest req; + ON_CALL(service, DeleteAllValues(_, _, _)) + .WillByDefault(Invoke( + [&](::grpc::ServerContext*, const DeleteAllValuesRequest* request, DeleteAllValuesResponse*) { + req = (*request); + return grpc::Status::OK; + })); + + ASSERT_THAT(store2->DeleteNamespace(IStore2::ScopeType::ACCOUNT, kAppId), Eq(WPEFramework::Core::ERROR_NONE)); + ASSERT_THAT(req.app_id(), Eq(kAppId)); + EXPECT_THAT(req.scope(), Eq(kScope)); +} diff --git a/PersistentStore/grpc/secure_storage.cmake b/PersistentStore/grpc/secure_storage.cmake new file mode 100644 index 0000000000..ae1eadadbd --- /dev/null +++ b/PersistentStore/grpc/secure_storage.cmake @@ -0,0 +1,67 @@ +# If not stated otherwise in this file or this component's LICENSE file the +# following copyright and licenses apply: +# +# Copyright 2020 RDK Management +# +# 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. + +FetchContent_Declare( + validate + GIT_REPOSITORY https://github.com/bufbuild/protoc-gen-validate.git + GIT_TAG v1.0.4 +) +FetchContent_Declare( + googleapis + GIT_REPOSITORY https://github.com/googleapis/googleapis.git + GIT_TAG master +) +FetchContent_MakeAvailable(validate googleapis) + +find_package(Protobuf REQUIRED) +protobuf_generate( + OUT_VAR PB_CC + PROTOS ${validate_SOURCE_DIR}/validate/validate.proto ${googleapis_SOURCE_DIR}/google/api/field_behavior.proto ${CMAKE_CURRENT_LIST_DIR}/secure_storage.proto + PROTOC_OUT_DIR ${CMAKE_CURRENT_BINARY_DIR} + IMPORT_DIRS ${validate_SOURCE_DIR} ${googleapis_SOURCE_DIR} ${CMAKE_CURRENT_LIST_DIR} +) +add_custom_target(pb_cc DEPENDS ${PB_CC}) +add_dependencies(${PROJECT_NAME} pb_cc) +target_link_libraries(${PROJECT_NAME} ${Protobuf_LIBRARIES}) + +find_package(PkgConfig REQUIRED) +pkg_search_module(gRPC REQUIRED grpc) +find_program(GRPC_CPP_PLUGIN grpc_cpp_plugin REQUIRED) +protobuf_generate( + OUT_VAR GRPC_PB_CC + PROTOS ${CMAKE_CURRENT_LIST_DIR}/secure_storage.proto + PROTOC_OUT_DIR ${CMAKE_CURRENT_BINARY_DIR} + IMPORT_DIRS ${CMAKE_CURRENT_LIST_DIR} ${validate_SOURCE_DIR} ${googleapis_SOURCE_DIR} + LANGUAGE grpc + PLUGIN protoc-gen-grpc=${GRPC_CPP_PLUGIN} + GENERATE_EXTENSIONS .grpc.pb.h .grpc.pb.cc +) +add_custom_target(grpc_pb_cc DEPENDS ${GRPC_PB_CC}) +add_dependencies(${PROJECT_NAME} grpc_pb_cc) +add_dependencies(grpc_pb_cc pb_cc) +target_link_libraries(${PROJECT_NAME} grpc++) +target_link_options(${PROJECT_NAME} PRIVATE "LINKER:--copy-dt-needed-entries") + +set(CC_SOURCES + ${CMAKE_CURRENT_BINARY_DIR}/validate/validate.pb.cc + ${CMAKE_CURRENT_BINARY_DIR}/google/api/field_behavior.pb.cc + ${CMAKE_CURRENT_BINARY_DIR}/secure_storage.pb.cc + ${CMAKE_CURRENT_BINARY_DIR}/secure_storage.grpc.pb.cc +) +target_sources(${PROJECT_NAME} PRIVATE ${CC_SOURCES}) +set_property(SOURCE ${CC_SOURCES} PROPERTY GENERATED 1) +target_include_directories(${PROJECT_NAME} PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) diff --git a/PersistentStore/grpc/secure_storage.proto b/PersistentStore/grpc/secure_storage.proto new file mode 100644 index 0000000000..d38eb1880d --- /dev/null +++ b/PersistentStore/grpc/secure_storage.proto @@ -0,0 +1,136 @@ +syntax = "proto3"; + +package distp.gateway.secure_storage.v1; + +import "google/api/field_behavior.proto"; +import "google/protobuf/duration.proto"; +import "google/protobuf/timestamp.proto"; +import "validate/validate.proto"; + +// SecureStorageService handles the storage and deletion of data (represented as string values) from applications given a particular key and scope. +service SecureStorageService { + // GetValue retrieves the value string stored in SecureStorage based on the scope and key provided for the current application. + rpc GetValue(GetValueRequest) returns (GetValueResponse); + // UpdateValue stores the string value provided in SecureStorage against the scope and key provided for the current application. + rpc UpdateValue(UpdateValueRequest) returns (UpdateValueResponse); + // DeleteValue removes the string value stored in SecureStorage, if present, defined by the scope and key provided for the current application/device. + rpc DeleteValue(DeleteValueRequest) returns (DeleteValueResponse); + // DeleteAllValues removes all values stored against the provided application for the given account. This includes all key/value pairs + // stored against the device scope for other devices within the account. Please note that this method does not take a key as input. + // Also, DeleteAllValues is a separate method given that access to delete all values may be controlled by explicit capabilities. + rpc DeleteAllValues(DeleteAllValuesRequest) returns (DeleteAllValuesResponse); + // SeedValue stores the string value provided in SecureStorage against the scope and key provided for the current application. + // This is a management API so the stored value will not cause the distributor's storage limits to be exceeded. + rpc SeedValue(SeedValueRequest) returns (SeedValueResponse); +} + +// Key is a group of fields that contribute to building the composite key that will be used to store the data in SecureStorage. +message Key { + // key of the key,value pair to be retrieved from SecureStorage. + string key = 1 [ + (google.api.field_behavior) = REQUIRED, + (validate.rules).string.min_len = 1 + ]; + // Scope describes the extent of the key,value pair. Scope is determined by the distributor. + Scope scope = 2 [ + (google.api.field_behavior) = REQUIRED, + (validate.rules).enum.defined_only = true, + (validate.rules).enum.not_in = 0 + ]; + + // app_id is the unique identifier of an app or family of apps. + string app_id = 3; +} + +// Value contains the value of the requested data as well as some other relevant fields like scope and expiry useful for the client. +message Value { + // key of the key,value pair that was retrieved from SecureStorage. + Key key = 1; + // value is the value associated with the key,value pair retrieved from SecureStorage. + string value = 2; + // expiration returns the expire time of the retrieved value. Conforms to AIP-214. + oneof expiration { + // Timestamp in UTC of when this resource is considered expired. + // This is *always* provided on output, regardless of what was sent on input. + google.protobuf.Timestamp expire_time = 3; + + // Input only. The TTL for this resource. + google.protobuf.Duration ttl = 4 [(google.api.field_behavior) = INPUT_ONLY]; + } +} + +// GetValueRequest is the request to retrieve the SecureStorage data. +message GetValueRequest { + // key is the group of fields that contribute to building the composite key that will be used to store the data in SecureStorage. + Key key = 1 [ + (google.api.field_behavior) = REQUIRED, + (validate.rules).message.required = true + ]; +} + +// GetValueResponse is the response containing the value of the requested key within the specified scope. +message GetValueResponse { + // value contains the data associated with the key,value pair that was stored in SecureStorage. + Value value = 1; +} + +// UpdateValueRequest is the request to store data in SecureStorage. +message UpdateValueRequest { + // key is the group of fields that contribute to building the composite key that will be used to store the data in SecureStorage. + Value value = 1 [ + (google.api.field_behavior) = REQUIRED, + (validate.rules).message.required = true + ]; +} + +// UpdateValueResponse is the response from the GetValue method. +message UpdateValueResponse {} + +// DeleteValueRequest is the request to remove a stored value given the key and scope. +message DeleteValueRequest { + // key is the group of fields that contribute to building the composite key that will be used to store the data in SecureStorage. + Key key = 1 [ + (google.api.field_behavior) = REQUIRED, + (validate.rules).message.required = true + ]; +} + +// DeleteValueResponse is the response from the DeleteValue method. +message DeleteValueResponse {} + +// DeleteAllValuesRequest is the request to delete all of the keys associated with an app under the given account. +message DeleteAllValuesRequest { + // app_id is the unique identifier of an app or family of apps. + string app_id = 1 [(google.api.field_behavior) = OPTIONAL]; + // Scope describes the extent of the key,value pair. Scope is determined by the distributor. + Scope scope = 2 [ + (google.api.field_behavior) = REQUIRED, + (validate.rules).enum.defined_only = true, + (validate.rules).enum.not_in = 0 + ]; +} + +// DeleteAllValuesResponse is the response from the DeleteAllValues method. +message DeleteAllValuesResponse {} + +// SeedValueRequest is the request to store data in SecureStorage. Stored data will not cause the distributor's storage limits to be exceeded. +message SeedValueRequest { + // value contains the data associated with the key,value pair that will be stored in SecureStorage. + Value value = 1 [ + (google.api.field_behavior) = REQUIRED, + (validate.rules).message.required = true + ]; +} + +// SeedValueResponse is the response from the SeedValue method. +message SeedValueResponse {} + +// Enumerated values for scope. +enum Scope { + // Represents an unset or invalid scope. + SCOPE_UNSPECIFIED = 0; + // Account scope. + SCOPE_ACCOUNT = 1; + // Device scope. + SCOPE_DEVICE = 2; +} diff --git a/PersistentStore/iarm/TimeSync.cpp b/PersistentStore/iarm/TimeSync.cpp new file mode 100644 index 0000000000..23fbd6b930 --- /dev/null +++ b/PersistentStore/iarm/TimeSync.cpp @@ -0,0 +1,10 @@ +#include "TimeSync.h" + +namespace WPEFramework { +namespace Plugin { + namespace Iarm { + using IarmTimeSync = TimeSync; + SERVICE_REGISTRATION(IarmTimeSync, 1, 0); + } // namespace Iarm +} // namespace Plugin +} // namespace WPEFramework diff --git a/PersistentStore/iarm/TimeSync.h b/PersistentStore/iarm/TimeSync.h new file mode 100644 index 0000000000..aa99627412 --- /dev/null +++ b/PersistentStore/iarm/TimeSync.h @@ -0,0 +1,103 @@ +/* + * If not stated otherwise in this file or this component's LICENSE file the + * following copyright and licenses apply: + * + * Copyright 2022 RDK Management + * + * 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 "../Module.h" +#include +#include +#include + +namespace WPEFramework { +namespace Plugin { + namespace Iarm { + + class TimeSync : public Exchange::ITimeSync { + private: + TimeSync(const TimeSync&) = delete; + TimeSync& operator=(const TimeSync&) = delete; + + public: + TimeSync() + : ITimeSync() + , _syncTime(0) + { + IARM_Bus_Init(IARM_INIT_NAME); + IARM_Bus_Connect(); + } + ~TimeSync() override = default; + + public: + void Register(INotification* notification) override + { + } + void Unregister(INotification* notification) override + { + } + uint32_t Synchronize() override + { + uint32_t result; + + IARM_Bus_SYSMgr_GetSystemStates_Param_t param; + memset(¶m, 0, sizeof(param)); + auto rc = IARM_Bus_Call(IARM_BUS_SYSMGR_NAME, IARM_BUS_SYSMGR_API_GetSystemStates, ¶m, sizeof(param)); + if (rc == IARM_RESULT_SUCCESS) { + if (param.time_source.state) { + _syncTime = Core::Time::Now().Ticks() / Core::Time::TicksPerMillisecond / 1000; + ; + result = Core::ERROR_NONE; + } else { + _syncTime = 0; + result = Core::ERROR_INPROGRESS; + } + } else { + OnError(__FUNCTION__, rc); + _syncTime = 0; + result = Core::ERROR_INCOMPLETE_CONFIG; + } + + return result; + } + void Cancel() override {} + string Source() const override + { + return string(); + } + uint64_t SyncTime() const override + { + return _syncTime; + } + + BEGIN_INTERFACE_MAP(TimeSync) + INTERFACE_ENTRY(ITimeSync) + END_INTERFACE_MAP + + private: + void OnError(const char* fn, const int status) const + { + TRACE(Trace::Error, (_T("%s iarm error %d"), status)); + } + + private: + std::atomic _syncTime; + }; + + } // namespace Iarm +} // namespace Plugin +} // namespace WPEFramework diff --git a/PersistentStore/l1test/CMakeLists.txt b/PersistentStore/l0test/CMakeLists.txt similarity index 76% rename from PersistentStore/l1test/CMakeLists.txt rename to PersistentStore/l0test/CMakeLists.txt index 4aa60125d4..d8f1e6ee20 100644 --- a/PersistentStore/l1test/CMakeLists.txt +++ b/PersistentStore/l0test/CMakeLists.txt @@ -15,9 +15,9 @@ # See the License for the specific language governing permissions and # limitations under the License. -project(persistentstorel1test) +cmake_minimum_required(VERSION 3.14) -cmake_minimum_required(VERSION 3.11) +project(persistentstorel0test) set(CMAKE_CXX_STANDARD 11) @@ -32,29 +32,17 @@ find_package(WPEFramework) find_package(${NAMESPACE}Plugins REQUIRED) find_package(${NAMESPACE}Definitions REQUIRED) -find_package(PkgConfig) -pkg_search_module(SQLITE REQUIRED sqlite3) - add_executable(${PROJECT_NAME} ../Module.cpp - ../sqlite/Handle.cpp ../PersistentStore.cpp ../PersistentStoreJsonRpc.cpp - JsonRpcTest.cpp - PluginTest.cpp - StoreTest.cpp - ) - -add_definitions(${SQLITE_CFLAGS_OTHER}) -link_directories(${SQLITE_LIBRARY_DIRS}) - -target_include_directories(${PROJECT_NAME} PRIVATE ../) + PersistentStoreTest.cpp +) target_link_libraries(${PROJECT_NAME} PRIVATE gmock_main ${NAMESPACE}Plugins::${NAMESPACE}Plugins ${NAMESPACE}Definitions::${NAMESPACE}Definitions - ${SQLITE_LIBRARIES} - ) +) install(TARGETS ${PROJECT_NAME} DESTINATION bin) diff --git a/PersistentStore/l0test/PersistentStoreTest.cpp b/PersistentStore/l0test/PersistentStoreTest.cpp new file mode 100644 index 0000000000..41a6e4a953 --- /dev/null +++ b/PersistentStore/l0test/PersistentStoreTest.cpp @@ -0,0 +1,656 @@ +#include +#include + +#include "../PersistentStore.h" +#include "ServiceMock.h" +#include "Store2Mock.h" +#include "StoreCacheMock.h" +#include "StoreInspectorMock.h" +#include "StoreLimitMock.h" + +using ::testing::_; +using ::testing::Eq; +using ::testing::Invoke; +using ::testing::IsFalse; +using ::testing::IsTrue; +using ::testing::Le; +using ::testing::NiceMock; +using ::testing::NotNull; +using ::testing::Return; +using ::testing::Test; +using ::WPEFramework::Core::PublishedServiceType; +using ::WPEFramework::Exchange::IStore; +using ::WPEFramework::Exchange::IStore2; +using ::WPEFramework::Exchange::IStoreInspector; +using ::WPEFramework::JsonData::PersistentStore::DeleteKeyParamsInfo; +using ::WPEFramework::JsonData::PersistentStore::DeleteNamespaceParamsInfo; +using ::WPEFramework::JsonData::PersistentStore::GetKeysResultData; +using ::WPEFramework::JsonData::PersistentStore::GetNamespacesResultData; +using ::WPEFramework::JsonData::PersistentStore::GetNamespaceStorageLimitResultData; +using ::WPEFramework::JsonData::PersistentStore::GetStorageSizesResultData; +using ::WPEFramework::JsonData::PersistentStore::GetValueResultData; +using ::WPEFramework::JsonData::PersistentStore::SetNamespaceStorageLimitParamsData; +using ::WPEFramework::JsonData::PersistentStore::SetValueParamsData; +using ::WPEFramework::Plugin::PersistentStore; +using ::WPEFramework::PluginHost::ILocalDispatcher; +using ::WPEFramework::PluginHost::IPlugin; +using ::WPEFramework::PluginHost::IShell; +using ::WPEFramework::RPC::IStringIterator; +using ::WPEFramework::RPC::IteratorType; +using ::WPEFramework::RPC::StringIterator; + +const auto kValue = "value_1"; +const auto kKey = "key_1"; +const auto kAppId = "app_id_1"; +const auto kTtl = 100; +const std::vector kKeys{ "key_1", "key_2" }; +const std::vector kAppIds{ "app_id_1", "app_id_2" }; +const std::vector kSizes{ { "app_id_1", 10 }, { "app_id_2", 20 } }; +const auto kSize = 1000; +const auto kFile1 = "/tmp/persistentstore/l0test/persistentstoretest1"; +const auto kFile2 = "/tmp/persistentstore/l0test/persistentstoretest2"; +const uint8_t kFileContent[12]{ 0x00, 0x01, 0x00, 0x00, 0x00, 0x06, 0xFE, 0x03, 0x01, 0xC1, 0x00, 0x01 }; +const auto kFileContentSize = sizeof(kFileContent) / sizeof(uint8_t); + +class APersistentStore : public Test { +protected: + NiceMock* service; + IPlugin* plugin; + APersistentStore() + : service(WPEFramework::Core::Service>::Create>()) + , plugin(WPEFramework::Core::Service::Create()) + { + } + ~APersistentStore() override + { + plugin->Release(); + service->Release(); + } +}; + +TEST_F(APersistentStore, GetsValueInDeviceScopeViaJsonRpc) +{ + class SqliteStore2 : public NiceMock { + public: + SqliteStore2() + { + EXPECT_CALL(*this, GetValue(_, _, _, _, _)) + .WillRepeatedly(Invoke( + [](const ScopeType scope, const string& ns, const string& key, string& value, uint32_t& ttl) { + EXPECT_THAT(scope, Eq(ScopeType::DEVICE)); + EXPECT_THAT(ns, Eq(kAppId)); + EXPECT_THAT(key, Eq(kKey)); + value = kValue; + ttl = kTtl; + return WPEFramework::Core::ERROR_NONE; + })); + } + }; + PublishedServiceType metadata(WPEFramework::Core::System::MODULE_NAME, 1, 0, 0); + ASSERT_THAT(plugin->Initialize(service), Eq("")); + auto jsonRpc = plugin->QueryInterface(); + ASSERT_THAT(jsonRpc, NotNull()); + DeleteKeyParamsInfo params; + params.Namespace = kAppId; + params.Key = kKey; + string paramsJsonStr; + params.ToString(paramsJsonStr); + string resultJsonStr; + ASSERT_THAT(jsonRpc->Invoke(0, 0, "", "getValue", paramsJsonStr, resultJsonStr), Eq(WPEFramework::Core::ERROR_NONE)); + GetValueResultData result; + result.FromString(resultJsonStr); + EXPECT_THAT(result.Value.Value(), Eq(kValue)); + EXPECT_THAT(result.Ttl.Value(), Eq(kTtl)); + jsonRpc->Release(); + plugin->Deinitialize(service); +} + +TEST_F(APersistentStore, GetsValueInAccountScopeViaJsonRpc) +{ + class GrpcStore2 : public NiceMock { + public: + GrpcStore2() + { + EXPECT_CALL(*this, GetValue(_, _, _, _, _)) + .WillRepeatedly(Invoke( + [](const ScopeType scope, const string& ns, const string& key, string& value, uint32_t& ttl) { + EXPECT_THAT(scope, Eq(ScopeType::ACCOUNT)); + EXPECT_THAT(ns, Eq(kAppId)); + EXPECT_THAT(key, Eq(kKey)); + value = kValue; + ttl = kTtl; + return WPEFramework::Core::ERROR_NONE; + })); + } + }; + PublishedServiceType metadata(WPEFramework::Core::System::MODULE_NAME, 1, 0, 0); + ASSERT_THAT(plugin->Initialize(service), Eq("")); + auto jsonRpc = plugin->QueryInterface(); + ASSERT_THAT(jsonRpc, NotNull()); + DeleteKeyParamsInfo params; + params.Scope = WPEFramework::JsonData::PersistentStore::ScopeType::ACCOUNT; + params.Namespace = kAppId; + params.Key = kKey; + string paramsJsonStr; + params.ToString(paramsJsonStr); + string resultJsonStr; + ASSERT_THAT(jsonRpc->Invoke(0, 0, "", "getValue", paramsJsonStr, resultJsonStr), Eq(WPEFramework::Core::ERROR_NONE)); + GetValueResultData result; + result.FromString(resultJsonStr); + EXPECT_THAT(result.Value.Value(), Eq(kValue)); + EXPECT_THAT(result.Ttl.Value(), Eq(kTtl)); + jsonRpc->Release(); + plugin->Deinitialize(service); +} + +TEST_F(APersistentStore, SetsValueInDeviceScopeViaJsonRpc) +{ + class SqliteStore2 : public NiceMock { + public: + SqliteStore2() + { + EXPECT_CALL(*this, SetValue(_, _, _, _, _)) + .WillRepeatedly(Invoke( + [](const ScopeType scope, const string& ns, const string& key, const string& value, const uint32_t ttl) { + EXPECT_THAT(scope, Eq(ScopeType::DEVICE)); + EXPECT_THAT(ns, Eq(kAppId)); + EXPECT_THAT(key, Eq(kKey)); + EXPECT_THAT(value, Eq(kValue)); + EXPECT_THAT(ttl, Eq(kTtl)); + return WPEFramework::Core::ERROR_NONE; + })); + } + }; + PublishedServiceType metadata(WPEFramework::Core::System::MODULE_NAME, 1, 0, 0); + ASSERT_THAT(plugin->Initialize(service), Eq("")); + auto jsonRpc = plugin->QueryInterface(); + ASSERT_THAT(jsonRpc, NotNull()); + SetValueParamsData params; + params.Namespace = kAppId; + params.Key = kKey; + params.Value = kValue; + params.Ttl = kTtl; + string paramsJsonStr; + params.ToString(paramsJsonStr); + string resultJsonStr; + EXPECT_THAT(jsonRpc->Invoke(0, 0, "", "setValue", paramsJsonStr, resultJsonStr), Eq(WPEFramework::Core::ERROR_NONE)); + jsonRpc->Release(); + plugin->Deinitialize(service); +} + +TEST_F(APersistentStore, SetsValueInAccountScopeViaJsonRpc) +{ + class GrpcStore2 : public NiceMock { + public: + GrpcStore2() + { + EXPECT_CALL(*this, SetValue(_, _, _, _, _)) + .WillRepeatedly(Invoke( + [](const ScopeType scope, const string& ns, const string& key, const string& value, const uint32_t ttl) { + EXPECT_THAT(scope, Eq(ScopeType::ACCOUNT)); + EXPECT_THAT(ns, Eq(kAppId)); + EXPECT_THAT(key, Eq(kKey)); + EXPECT_THAT(value, Eq(kValue)); + EXPECT_THAT(ttl, Eq(kTtl)); + return WPEFramework::Core::ERROR_NONE; + })); + } + }; + PublishedServiceType metadata(WPEFramework::Core::System::MODULE_NAME, 1, 0, 0); + ASSERT_THAT(plugin->Initialize(service), Eq("")); + auto jsonRpc = plugin->QueryInterface(); + ASSERT_THAT(jsonRpc, NotNull()); + SetValueParamsData params; + params.Scope = WPEFramework::JsonData::PersistentStore::ScopeType::ACCOUNT; + params.Namespace = kAppId; + params.Key = kKey; + params.Value = kValue; + params.Ttl = kTtl; + string paramsJsonStr; + params.ToString(paramsJsonStr); + string resultJsonStr; + EXPECT_THAT(jsonRpc->Invoke(0, 0, "", "setValue", paramsJsonStr, resultJsonStr), Eq(WPEFramework::Core::ERROR_NONE)); + jsonRpc->Release(); + plugin->Deinitialize(service); +} + +TEST_F(APersistentStore, DeletesKeyInDeviceScopeViaJsonRpc) +{ + class SqliteStore2 : public NiceMock { + public: + SqliteStore2() + { + EXPECT_CALL(*this, DeleteKey(_, _, _)) + .WillRepeatedly(Invoke( + [](const ScopeType scope, const string& ns, const string& key) { + EXPECT_THAT(scope, Eq(ScopeType::DEVICE)); + EXPECT_THAT(ns, Eq(kAppId)); + EXPECT_THAT(key, Eq(kKey)); + return WPEFramework::Core::ERROR_NONE; + })); + } + }; + PublishedServiceType metadata(WPEFramework::Core::System::MODULE_NAME, 1, 0, 0); + ASSERT_THAT(plugin->Initialize(service), Eq("")); + auto jsonRpc = plugin->QueryInterface(); + ASSERT_THAT(jsonRpc, NotNull()); + DeleteKeyParamsInfo params; + params.Namespace = kAppId; + params.Key = kKey; + string paramsJsonStr; + params.ToString(paramsJsonStr); + string resultJsonStr; + EXPECT_THAT(jsonRpc->Invoke(0, 0, "", "deleteKey", paramsJsonStr, resultJsonStr), Eq(WPEFramework::Core::ERROR_NONE)); + jsonRpc->Release(); + plugin->Deinitialize(service); +} + +TEST_F(APersistentStore, DeletesKeyInAccountScopeViaJsonRpc) +{ + class GrpcStore2 : public NiceMock { + public: + GrpcStore2() + { + EXPECT_CALL(*this, DeleteKey(_, _, _)) + .WillRepeatedly(Invoke( + [](const ScopeType scope, const string& ns, const string& key) { + EXPECT_THAT(scope, Eq(ScopeType::ACCOUNT)); + EXPECT_THAT(ns, Eq(kAppId)); + EXPECT_THAT(key, Eq(kKey)); + return WPEFramework::Core::ERROR_NONE; + })); + } + }; + PublishedServiceType metadata(WPEFramework::Core::System::MODULE_NAME, 1, 0, 0); + ASSERT_THAT(plugin->Initialize(service), Eq("")); + auto jsonRpc = plugin->QueryInterface(); + ASSERT_THAT(jsonRpc, NotNull()); + DeleteKeyParamsInfo params; + params.Scope = WPEFramework::JsonData::PersistentStore::ScopeType::ACCOUNT; + params.Namespace = kAppId; + params.Key = kKey; + string paramsJsonStr; + params.ToString(paramsJsonStr); + string resultJsonStr; + EXPECT_THAT(jsonRpc->Invoke(0, 0, "", "deleteKey", paramsJsonStr, resultJsonStr), Eq(WPEFramework::Core::ERROR_NONE)); + jsonRpc->Release(); + plugin->Deinitialize(service); +} + +TEST_F(APersistentStore, DeletesNamespaceInDeviceScopeViaJsonRpc) +{ + class SqliteStore2 : public NiceMock { + public: + SqliteStore2() + { + EXPECT_CALL(*this, DeleteNamespace(_, _)) + .WillRepeatedly(Invoke( + [](const ScopeType scope, const string& ns) { + EXPECT_THAT(scope, Eq(ScopeType::DEVICE)); + EXPECT_THAT(ns, Eq(kAppId)); + return WPEFramework::Core::ERROR_NONE; + })); + } + }; + PublishedServiceType metadata(WPEFramework::Core::System::MODULE_NAME, 1, 0, 0); + ASSERT_THAT(plugin->Initialize(service), Eq("")); + auto jsonRpc = plugin->QueryInterface(); + ASSERT_THAT(jsonRpc, NotNull()); + DeleteNamespaceParamsInfo params; + params.Namespace = kAppId; + string paramsJsonStr; + params.ToString(paramsJsonStr); + string resultJsonStr; + EXPECT_THAT(jsonRpc->Invoke(0, 0, "", "deleteNamespace", paramsJsonStr, resultJsonStr), Eq(WPEFramework::Core::ERROR_NONE)); + jsonRpc->Release(); + plugin->Deinitialize(service); +} + +TEST_F(APersistentStore, DeletesNamespaceInAccountScopeViaJsonRpc) +{ + class GrpcStore2 : public NiceMock { + public: + GrpcStore2() + { + EXPECT_CALL(*this, DeleteNamespace(_, _)) + .WillRepeatedly(Invoke( + [](const ScopeType scope, const string& ns) { + EXPECT_THAT(scope, Eq(ScopeType::ACCOUNT)); + EXPECT_THAT(ns, Eq(kAppId)); + return WPEFramework::Core::ERROR_NONE; + })); + } + }; + PublishedServiceType metadata(WPEFramework::Core::System::MODULE_NAME, 1, 0, 0); + ASSERT_THAT(plugin->Initialize(service), Eq("")); + auto jsonRpc = plugin->QueryInterface(); + ASSERT_THAT(jsonRpc, NotNull()); + DeleteNamespaceParamsInfo params; + params.Scope = WPEFramework::JsonData::PersistentStore::ScopeType::ACCOUNT; + params.Namespace = kAppId; + string paramsJsonStr; + params.ToString(paramsJsonStr); + string resultJsonStr; + EXPECT_THAT(jsonRpc->Invoke(0, 0, "", "deleteNamespace", paramsJsonStr, resultJsonStr), Eq(WPEFramework::Core::ERROR_NONE)); + jsonRpc->Release(); + plugin->Deinitialize(service); +} + +TEST_F(APersistentStore, FlushesCacheViaJsonRpc) +{ + class SqliteStoreCache : public NiceMock { + public: + SqliteStoreCache() + { + EXPECT_CALL(*this, FlushCache()) + .WillRepeatedly(Return(WPEFramework::Core::ERROR_NONE)); + } + }; + PublishedServiceType metadata(WPEFramework::Core::System::MODULE_NAME, 1, 0, 0); + ASSERT_THAT(plugin->Initialize(service), Eq("")); + auto jsonRpc = plugin->QueryInterface(); + ASSERT_THAT(jsonRpc, NotNull()); + string resultJsonStr; + EXPECT_THAT(jsonRpc->Invoke(0, 0, "", "flushCache", "", resultJsonStr), Eq(WPEFramework::Core::ERROR_NONE)); + jsonRpc->Release(); + plugin->Deinitialize(service); +} + +TEST_F(APersistentStore, GetsKeysInDeviceScopeViaJsonRpc) +{ + class SqliteStoreInspector : public NiceMock { + public: + SqliteStoreInspector() + { + EXPECT_CALL(*this, GetKeys(_, _, _)) + .WillRepeatedly(Invoke( + [](const ScopeType scope, const string& ns, IStringIterator*& keys) { + EXPECT_THAT(scope, Eq(ScopeType::DEVICE)); + EXPECT_THAT(ns, Eq(kAppId)); + keys = (WPEFramework::Core::Service::Create(kKeys)); + return WPEFramework::Core::ERROR_NONE; + })); + } + }; + PublishedServiceType metadata(WPEFramework::Core::System::MODULE_NAME, 1, 0, 0); + ASSERT_THAT(plugin->Initialize(service), Eq("")); + auto jsonRpc = plugin->QueryInterface(); + ASSERT_THAT(jsonRpc, NotNull()); + DeleteNamespaceParamsInfo params; + params.Namespace = kAppId; + string paramsJsonStr; + params.ToString(paramsJsonStr); + string resultJsonStr; + ASSERT_THAT(jsonRpc->Invoke(0, 0, "", "getKeys", paramsJsonStr, resultJsonStr), Eq(WPEFramework::Core::ERROR_NONE)); + GetKeysResultData result; + result.FromString(resultJsonStr); + auto index(result.Keys.Elements()); + ASSERT_THAT(index.Next(), IsTrue()); + EXPECT_THAT(index.Current().Value(), Eq(kKeys.at(0))); + ASSERT_THAT(index.Next(), IsTrue()); + EXPECT_THAT(index.Current().Value(), Eq(kKeys.at(1))); + EXPECT_THAT(index.Next(), IsFalse()); + jsonRpc->Release(); + plugin->Deinitialize(service); +} + +TEST_F(APersistentStore, GetsNamespacesInDeviceScopeViaJsonRpc) +{ + class SqliteStoreInspector : public NiceMock { + public: + SqliteStoreInspector() + { + EXPECT_CALL(*this, GetNamespaces(_, _)) + .WillRepeatedly(Invoke( + [](const ScopeType scope, IStringIterator*& namespaces) { + EXPECT_THAT(scope, Eq(ScopeType::DEVICE)); + namespaces = (WPEFramework::Core::Service::Create(kAppIds)); + return WPEFramework::Core::ERROR_NONE; + })); + } + }; + PublishedServiceType metadata(WPEFramework::Core::System::MODULE_NAME, 1, 0, 0); + ASSERT_THAT(plugin->Initialize(service), Eq("")); + auto jsonRpc = plugin->QueryInterface(); + ASSERT_THAT(jsonRpc, NotNull()); + string resultJsonStr; + ASSERT_THAT(jsonRpc->Invoke(0, 0, "", "getNamespaces", "", resultJsonStr), Eq(WPEFramework::Core::ERROR_NONE)); + GetNamespacesResultData result; + result.FromString(resultJsonStr); + auto index(result.Namespaces.Elements()); + ASSERT_THAT(index.Next(), IsTrue()); + EXPECT_THAT(index.Current().Value(), Eq(kAppIds.at(0))); + ASSERT_THAT(index.Next(), IsTrue()); + EXPECT_THAT(index.Current().Value(), Eq(kAppIds.at(1))); + EXPECT_THAT(index.Next(), IsFalse()); + jsonRpc->Release(); + plugin->Deinitialize(service); +} + +TEST_F(APersistentStore, GetsStorageSizesInDeviceScopeViaJsonRpc) +{ + class SqliteStoreInspector : public NiceMock { + public: + SqliteStoreInspector() + { + EXPECT_CALL(*this, GetStorageSizes(_, _)) + .WillRepeatedly(Invoke( + [](const ScopeType scope, INamespaceSizeIterator*& storageList) { + EXPECT_THAT(scope, Eq(ScopeType::DEVICE)); + storageList = (WPEFramework::Core::Service>::Create(kSizes)); + return WPEFramework::Core::ERROR_NONE; + })); + } + }; + PublishedServiceType metadata(WPEFramework::Core::System::MODULE_NAME, 1, 0, 0); + ASSERT_THAT(plugin->Initialize(service), Eq("")); + auto jsonRpc = plugin->QueryInterface(); + ASSERT_THAT(jsonRpc, NotNull()); + string resultJsonStr; + ASSERT_THAT(jsonRpc->Invoke(0, 0, "", "getStorageSizes", "", resultJsonStr), Eq(WPEFramework::Core::ERROR_NONE)); + GetStorageSizesResultData result; + result.FromString(resultJsonStr); + auto index(result.StorageList.Elements()); + ASSERT_THAT(index.Next(), IsTrue()); + EXPECT_THAT(index.Current().Namespace.Value(), Eq(kSizes.at(0).ns)); + EXPECT_THAT(index.Current().Size.Value(), Eq(kSizes.at(0).size)); + ASSERT_THAT(index.Next(), IsTrue()); + EXPECT_THAT(index.Current().Namespace.Value(), Eq(kSizes.at(1).ns)); + EXPECT_THAT(index.Current().Size.Value(), Eq(kSizes.at(1).size)); + EXPECT_THAT(index.Next(), IsFalse()); + jsonRpc->Release(); + plugin->Deinitialize(service); +} + +TEST_F(APersistentStore, GetsNamespaceStorageLimitInDeviceScopeViaJsonRpc) +{ + class SqliteStoreLimit : public NiceMock { + public: + SqliteStoreLimit() + { + EXPECT_CALL(*this, GetNamespaceStorageLimit(_, _, _)) + .WillRepeatedly(Invoke( + [](const ScopeType scope, const string& ns, uint32_t& size) { + EXPECT_THAT(scope, Eq(ScopeType::DEVICE)); + EXPECT_THAT(ns, Eq(kAppId)); + size = kSize; + return WPEFramework::Core::ERROR_NONE; + })); + } + }; + PublishedServiceType metadata(WPEFramework::Core::System::MODULE_NAME, 1, 0, 0); + ASSERT_THAT(plugin->Initialize(service), Eq("")); + auto jsonRpc = plugin->QueryInterface(); + ASSERT_THAT(jsonRpc, NotNull()); + DeleteNamespaceParamsInfo params; + params.Namespace = kAppId; + string paramsJsonStr; + params.ToString(paramsJsonStr); + string resultJsonStr; + ASSERT_THAT(jsonRpc->Invoke(0, 0, "", "getNamespaceStorageLimit", paramsJsonStr, resultJsonStr), Eq(WPEFramework::Core::ERROR_NONE)); + GetNamespaceStorageLimitResultData result; + result.FromString(resultJsonStr); + EXPECT_THAT(result.StorageLimit.Value(), Eq(kSize)); + jsonRpc->Release(); + plugin->Deinitialize(service); +} + +TEST_F(APersistentStore, SetsNamespaceStorageLimitInDeviceScopeViaJsonRpc) +{ + class SqliteStoreLimit : public NiceMock { + public: + SqliteStoreLimit() + { + EXPECT_CALL(*this, SetNamespaceStorageLimit(_, _, _)) + .WillRepeatedly(Invoke( + [](const ScopeType scope, const string& ns, const uint32_t size) { + EXPECT_THAT(scope, Eq(ScopeType::DEVICE)); + EXPECT_THAT(ns, Eq(kAppId)); + EXPECT_THAT(size, Eq(kSize)); + return WPEFramework::Core::ERROR_NONE; + })); + } + }; + PublishedServiceType metadata(WPEFramework::Core::System::MODULE_NAME, 1, 0, 0); + ASSERT_THAT(plugin->Initialize(service), Eq("")); + auto jsonRpc = plugin->QueryInterface(); + ASSERT_THAT(jsonRpc, NotNull()); + SetNamespaceStorageLimitParamsData params; + params.Namespace = kAppId; + params.StorageLimit = kSize; + string paramsJsonStr; + params.ToString(paramsJsonStr); + string resultJsonStr; + EXPECT_THAT(jsonRpc->Invoke(0, 0, "", "setNamespaceStorageLimit", paramsJsonStr, resultJsonStr), Eq(WPEFramework::Core::ERROR_NONE)); + jsonRpc->Release(); + plugin->Deinitialize(service); +} + +TEST_F(APersistentStore, GetsValueInDeviceScopeViaIStore) +{ + class SqliteStore2 : public NiceMock { + public: + SqliteStore2() + { + EXPECT_CALL(*this, GetValue(_, _, _, _, _)) + .WillRepeatedly(Invoke( + [](const ScopeType scope, const string& ns, const string& key, string& value, uint32_t& ttl) { + EXPECT_THAT(scope, Eq(ScopeType::DEVICE)); + EXPECT_THAT(ns, Eq(kAppId)); + EXPECT_THAT(key, Eq(kKey)); + value = kValue; + return WPEFramework::Core::ERROR_NONE; + })); + } + }; + PublishedServiceType metadata(WPEFramework::Core::System::MODULE_NAME, 1, 0, 0); + ASSERT_THAT(plugin->Initialize(service), Eq("")); + auto store = plugin->QueryInterface(); + ASSERT_THAT(store, NotNull()); + string value; + ASSERT_THAT(store->GetValue(kAppId, kKey, value), Eq(WPEFramework::Core::ERROR_NONE)); + EXPECT_THAT(value, Eq(kValue)); + store->Release(); + plugin->Deinitialize(service); +} + +TEST_F(APersistentStore, SetsValueInDeviceScopeViaIStore) +{ + class SqliteStore2 : public NiceMock { + public: + SqliteStore2() + { + EXPECT_CALL(*this, SetValue(_, _, _, _, _)) + .WillRepeatedly(Invoke( + [](const ScopeType scope, const string& ns, const string& key, const string& value, const uint32_t ttl) { + EXPECT_THAT(scope, Eq(ScopeType::DEVICE)); + EXPECT_THAT(ns, Eq(kAppId)); + EXPECT_THAT(key, Eq(kKey)); + EXPECT_THAT(value, Eq(kValue)); + EXPECT_THAT(ttl, Eq(0)); + return WPEFramework::Core::ERROR_NONE; + })); + } + }; + PublishedServiceType metadata(WPEFramework::Core::System::MODULE_NAME, 1, 0, 0); + ASSERT_THAT(plugin->Initialize(service), Eq("")); + auto store = plugin->QueryInterface(); + ASSERT_THAT(store, NotNull()); + EXPECT_THAT(store->SetValue(kAppId, kKey, kValue), Eq(WPEFramework::Core::ERROR_NONE)); + store->Release(); + plugin->Deinitialize(service); +} + +TEST_F(APersistentStore, DeletesKeyInDeviceScopeViaIStore) +{ + class SqliteStore2 : public NiceMock { + public: + SqliteStore2() + { + EXPECT_CALL(*this, DeleteKey(_, _, _)) + .WillRepeatedly(Invoke( + [](const ScopeType scope, const string& ns, const string& key) { + EXPECT_THAT(scope, Eq(ScopeType::DEVICE)); + EXPECT_THAT(ns, Eq(kAppId)); + EXPECT_THAT(key, Eq(kKey)); + return WPEFramework::Core::ERROR_NONE; + })); + } + }; + PublishedServiceType metadata(WPEFramework::Core::System::MODULE_NAME, 1, 0, 0); + ASSERT_THAT(plugin->Initialize(service), Eq("")); + auto store = plugin->QueryInterface(); + ASSERT_THAT(store, NotNull()); + EXPECT_THAT(store->DeleteKey(kAppId, kKey), Eq(WPEFramework::Core::ERROR_NONE)); + store->Release(); + plugin->Deinitialize(service); +} + +TEST_F(APersistentStore, DeletesNamespaceInDeviceScopeViaIStore) +{ + class SqliteStore2 : public NiceMock { + public: + SqliteStore2() + { + EXPECT_CALL(*this, DeleteNamespace(_, _)) + .WillRepeatedly(Invoke( + [](const ScopeType scope, const string& ns) { + EXPECT_THAT(scope, Eq(ScopeType::DEVICE)); + EXPECT_THAT(ns, Eq(kAppId)); + return WPEFramework::Core::ERROR_NONE; + })); + } + }; + PublishedServiceType metadata(WPEFramework::Core::System::MODULE_NAME, 1, 0, 0); + ASSERT_THAT(plugin->Initialize(service), Eq("")); + auto store = plugin->QueryInterface(); + ASSERT_THAT(store, NotNull()); + EXPECT_THAT(store->DeleteNamespace(kAppId), Eq(WPEFramework::Core::ERROR_NONE)); + store->Release(); + plugin->Deinitialize(service); +} + +TEST_F(APersistentStore, MovesFileWhenInitializedWithNewAndPreviousPath) +{ + WPEFramework::Core::File file1(kFile1); + WPEFramework::Core::File file2(kFile2); + file1.Destroy(); + file2.Destroy(); + WPEFramework::Core::Directory(file1.PathName().c_str()).CreatePath(); + ASSERT_THAT(file1.Create(), IsTrue()); + file1.Write(kFileContent, kFileContentSize); + JsonObject config; + config["path"] = kFile2; + config["legacypath"] = kFile1; + string configJsonStr; + config.ToString(configJsonStr); + ON_CALL(*service, ConfigLine()) + .WillByDefault(Return(configJsonStr)); + ASSERT_THAT(plugin->Initialize(service), Eq("")); + plugin->Deinitialize(service); + ASSERT_THAT(WPEFramework::Core::File(kFile1).Exists(), IsFalse()); + ASSERT_THAT(file2.Open(true), IsTrue()); + uint8_t buffer[1024]; + ASSERT_THAT(kFileContentSize, file2.Read(buffer, 1024)); + EXPECT_THAT(memcmp(buffer, kFileContent, kFileContentSize), Eq(0)); +} diff --git a/PersistentStore/l1test/ServiceMock.h b/PersistentStore/l0test/ServiceMock.h similarity index 93% rename from PersistentStore/l1test/ServiceMock.h rename to PersistentStore/l0test/ServiceMock.h index 4c8c01109a..f18d2c9c29 100644 --- a/PersistentStore/l1test/ServiceMock.h +++ b/PersistentStore/l0test/ServiceMock.h @@ -1,15 +1,11 @@ #pragma once -#include - #include "../Module.h" +#include class ServiceMock : public WPEFramework::PluginHost::IShell { public: - virtual ~ServiceMock() override = default; - - MOCK_METHOD(void, AddRef, (), (const, override)); - MOCK_METHOD(uint32_t, Release, (), (const, override)); + ~ServiceMock() override = default; MOCK_METHOD(string, Versions, (), (const, override)); MOCK_METHOD(string, Locator, (), (const, override)); MOCK_METHOD(string, ClassName, (), (const, override)); @@ -27,7 +23,6 @@ class ServiceMock : public WPEFramework::PluginHost::IShell { MOCK_METHOD(WPEFramework::PluginHost::ISubSystem*, SubSystems, (), (override)); MOCK_METHOD(uint32_t, Submit, (const uint32_t, const WPEFramework::Core::ProxyType&), (override)); MOCK_METHOD(void, Notify, (const string&), (override)); - MOCK_METHOD(void*, QueryInterface, (const uint32_t), (override)); MOCK_METHOD(void*, QueryInterfaceByCallsign, (const uint32_t, const string&), (override)); MOCK_METHOD(void, Register, (WPEFramework::PluginHost::IPlugin::INotification*), (override)); MOCK_METHOD(void, Unregister, (WPEFramework::PluginHost::IPlugin::INotification*), (override)); @@ -52,4 +47,7 @@ class ServiceMock : public WPEFramework::PluginHost::IShell { MOCK_METHOD(WPEFramework::Core::hresult, Resumed, (const bool value), (override)); MOCK_METHOD(WPEFramework::Core::hresult, Metadata, (string & info /* @out */), (const, override)); MOCK_METHOD(WPEFramework::Core::hresult, Hibernate, (const uint32_t timeout), (override)); + BEGIN_INTERFACE_MAP(ServiceMock) + INTERFACE_ENTRY(IShell) + END_INTERFACE_MAP }; diff --git a/PersistentStore/l0test/Store2Mock.h b/PersistentStore/l0test/Store2Mock.h new file mode 100644 index 0000000000..35ac62674f --- /dev/null +++ b/PersistentStore/l0test/Store2Mock.h @@ -0,0 +1,18 @@ +#pragma once + +#include +#include + +class Store2Mock : public WPEFramework::Exchange::IStore2 { +public: + ~Store2Mock() override = default; + MOCK_METHOD(uint32_t, Register, (INotification*), (override)); + MOCK_METHOD(uint32_t, Unregister, (INotification*), (override)); + MOCK_METHOD(uint32_t, SetValue, (const ScopeType scope, const string& ns, const string& key, const string& value, const uint32_t ttl), (override)); + MOCK_METHOD(uint32_t, GetValue, (const ScopeType scope, const string& ns, const string& key, string& value, uint32_t& ttl), (override)); + MOCK_METHOD(uint32_t, DeleteKey, (const ScopeType scope, const string& ns, const string& key), (override)); + MOCK_METHOD(uint32_t, DeleteNamespace, (const ScopeType scope, const string& ns), (override)); + BEGIN_INTERFACE_MAP(Store2Mock) + INTERFACE_ENTRY(IStore2) + END_INTERFACE_MAP +}; diff --git a/PersistentStore/l0test/StoreCacheMock.h b/PersistentStore/l0test/StoreCacheMock.h new file mode 100644 index 0000000000..ed7715a013 --- /dev/null +++ b/PersistentStore/l0test/StoreCacheMock.h @@ -0,0 +1,13 @@ +#pragma once + +#include +#include + +class StoreCacheMock : public WPEFramework::Exchange::IStoreCache { +public: + ~StoreCacheMock() override = default; + MOCK_METHOD(uint32_t, FlushCache, (), (override)); + BEGIN_INTERFACE_MAP(StoreCacheMock) + INTERFACE_ENTRY(IStoreCache) + END_INTERFACE_MAP +}; diff --git a/PersistentStore/l0test/StoreInspectorMock.h b/PersistentStore/l0test/StoreInspectorMock.h new file mode 100644 index 0000000000..7765f744a7 --- /dev/null +++ b/PersistentStore/l0test/StoreInspectorMock.h @@ -0,0 +1,15 @@ +#pragma once + +#include +#include + +class StoreInspectorMock : public WPEFramework::Exchange::IStoreInspector { +public: + ~StoreInspectorMock() override = default; + MOCK_METHOD(uint32_t, GetKeys, (const ScopeType scope, const string& ns, IStringIterator*& keys), (override)); + MOCK_METHOD(uint32_t, GetNamespaces, (const ScopeType scope, IStringIterator*& namespaces), (override)); + MOCK_METHOD(uint32_t, GetStorageSizes, (const ScopeType scope, INamespaceSizeIterator*& storageList), (override)); + BEGIN_INTERFACE_MAP(StoreInspectorMock) + INTERFACE_ENTRY(IStoreInspector) + END_INTERFACE_MAP +}; diff --git a/PersistentStore/l0test/StoreLimitMock.h b/PersistentStore/l0test/StoreLimitMock.h new file mode 100644 index 0000000000..2b818984e9 --- /dev/null +++ b/PersistentStore/l0test/StoreLimitMock.h @@ -0,0 +1,14 @@ +#pragma once + +#include +#include + +class StoreLimitMock : public WPEFramework::Exchange::IStoreLimit { +public: + ~StoreLimitMock() override = default; + MOCK_METHOD(uint32_t, GetNamespaceStorageLimit, (const ScopeType scope, const string& ns, uint32_t& size), (override)); + MOCK_METHOD(uint32_t, SetNamespaceStorageLimit, (const ScopeType scope, const string& ns, const uint32_t size), (override)); + BEGIN_INTERFACE_MAP(StoreLimitMock) + INTERFACE_ENTRY(IStoreLimit) + END_INTERFACE_MAP +}; diff --git a/PersistentStore/l1test/JsonRpcTest.cpp b/PersistentStore/l1test/JsonRpcTest.cpp deleted file mode 100644 index c090fc31c2..0000000000 --- a/PersistentStore/l1test/JsonRpcTest.cpp +++ /dev/null @@ -1,265 +0,0 @@ -#include -#include - -#include "../PersistentStore.h" - -#include "ServiceMock.h" - -using namespace WPEFramework; -using namespace WPEFramework::Plugin; - -using ::testing::Eq; -using ::testing::IsFalse; -using ::testing::IsTrue; -using ::testing::NiceMock; -using ::testing::NotNull; -using ::testing::Test; - -const std::string Path = "/tmp/persistentstore/l1test/jsonrpctest"; -const uint32_t MaxSize = 100; -const uint32_t MaxValue = 10; -const uint32_t Limit = 50; - -class AJsonRpc : public Test { -protected: - NiceMock service; - PluginHost::IPlugin* plugin; - PluginHost::ILocalDispatcher* jsonRpc; - ~AJsonRpc() override = default; - void SetUp() override - { - Core::File(Path).Destroy(); - // File is destroyed - - plugin = Core::Service::Create(); - jsonRpc = plugin->QueryInterface(); - ASSERT_THAT(jsonRpc, NotNull()); - JsonObject config; - config["path"] = Path; - config["maxsize"] = MaxSize; - config["maxvalue"] = MaxValue; - config["limit"] = Limit; - string configJsonStr; - config.ToString(configJsonStr); - ON_CALL(service, ConfigLine()) - .WillByDefault( - ::testing::Return(configJsonStr)); - ASSERT_THAT(plugin->Initialize(&service), Eq("")); - } - void TearDown() override - { - plugin->Deinitialize(&service); - if (jsonRpc) - jsonRpc->Release(); - plugin->Release(); - } -}; - -TEST_F(AJsonRpc, DoesNotGetValueInUnknownNamespace) -{ - string response; - EXPECT_THAT(jsonRpc->Invoke(0, 0, "", "getValue", "{\"namespace\":\"x\",\"key\":\"x\"}", response), Eq(Core::ERROR_NOT_EXIST)); -} - -TEST_F(AJsonRpc, DoesNotGetValueWhenMandatoryParamsAreMissing) -{ - string response; - EXPECT_THAT(jsonRpc->Invoke(0, 0, "", "getValue", "{\"key\":\"x\"}", response), Eq(Core::ERROR_INVALID_INPUT_LENGTH)); - EXPECT_THAT(jsonRpc->Invoke(0, 0, "", "getValue", "{\"namespace\":\"x\"}", response), Eq(Core::ERROR_INVALID_INPUT_LENGTH)); -} - -TEST_F(AJsonRpc, DoesNotSetEmptyNamespace) -{ - string response; - EXPECT_THAT(jsonRpc->Invoke(0, 0, "", "setValue", "{\"namespace\":\"\",\"key\":\"x\",\"value\":\"x\"}", response), Eq(Core::ERROR_INVALID_INPUT_LENGTH)); -} - -TEST_F(AJsonRpc, DoesNotSetEmptyKey) -{ - string response; - EXPECT_THAT(jsonRpc->Invoke(0, 0, "", "setValue", "{\"namespace\":\"x\",\"key\":\"\",\"value\":\"x\"}", response), Eq(Core::ERROR_INVALID_INPUT_LENGTH)); -} - -TEST_F(AJsonRpc, DoesNotSetWhenMandatoryParamsAreMissing) -{ - string response; - EXPECT_THAT(jsonRpc->Invoke(0, 0, "", "setValue", "{\"key\":\"x\",\"value\":\"x\"}", response), Eq(Core::ERROR_INVALID_INPUT_LENGTH)); - EXPECT_THAT(jsonRpc->Invoke(0, 0, "", "setValue", "{\"namespace\":\"x\",\"value\":\"x\"}", response), Eq(Core::ERROR_INVALID_INPUT_LENGTH)); - EXPECT_THAT(jsonRpc->Invoke(0, 0, "", "setValue", "{\"namespace\":\"x\",\"key\":\"x\"}", response), Eq(Core::ERROR_INVALID_INPUT_LENGTH)); -} - -TEST_F(AJsonRpc, DoesNotGetKeysWhenMandatoryParamsAreMissing) -{ - string response; - EXPECT_THAT(jsonRpc->Invoke(0, 0, "", "getKeys", "", response), Eq(Core::ERROR_INVALID_INPUT_LENGTH)); -} - -TEST_F(AJsonRpc, GetsKeysForUnknownNamespaceWithoutError) -{ - string response; - EXPECT_THAT(jsonRpc->Invoke(0, 0, "", "getKeys", "{\"namespace\":\"x\"}", response), Eq(Core::ERROR_NONE)); - EXPECT_THAT(response, Eq("{\"success\":true}")); -} - -TEST_F(AJsonRpc, GetsNamespacesWhenEmptyWithoutError) -{ - string response; - EXPECT_THAT(jsonRpc->Invoke(0, 0, "", "getNamespaces", "", response), Eq(Core::ERROR_NONE)); - EXPECT_THAT(response, Eq("{\"success\":true}")); -} - -TEST_F(AJsonRpc, GetsStorageSizesWhenEmptyWithoutError) -{ - string response; - EXPECT_THAT(jsonRpc->Invoke(0, 0, "", "getStorageSizes", "", response), Eq(Core::ERROR_NONE)); - EXPECT_THAT(response, Eq("{}")); -} - -// Deprecated -TEST_F(AJsonRpc, GetsStorageSizeWhenEmptyWithoutError) -{ - string response; - EXPECT_THAT(jsonRpc->Invoke(0, 0, "", "getStorageSize", "", response), Eq(Core::ERROR_NONE)); - EXPECT_THAT(response, Eq("{\"namespaceSizes\":{},\"success\":true}")); -} - -TEST_F(AJsonRpc, DoesNotGetLimitForUnknownNamespace) -{ - string response; - EXPECT_THAT(jsonRpc->Invoke(0, 0, "", "getNamespaceStorageLimit", "{\"namespace\":\"x\"}", response), Eq(Core::ERROR_NOT_EXIST)); -} - -TEST_F(AJsonRpc, DoesNotGetLimitWhenMandatoryParamsAreMissing) -{ - string response; - EXPECT_THAT(jsonRpc->Invoke(0, 0, "", "getNamespaceStorageLimit", "", response), Eq(Core::ERROR_INVALID_INPUT_LENGTH)); -} - -TEST_F(AJsonRpc, DoesNotSetLimitWhenMandatoryParamsAreMissing) -{ - string response; - EXPECT_THAT(jsonRpc->Invoke(0, 0, "", "setNamespaceStorageLimit", "", response), Eq(Core::ERROR_INVALID_INPUT_LENGTH)); -} - -TEST_F(AJsonRpc, DoesNotDeleteKeyWhenMandatoryParamsAreMissing) -{ - string response; - EXPECT_THAT(jsonRpc->Invoke(0, 0, "", "deleteKey", "{\"key\":\"x\"}", response), Eq(Core::ERROR_INVALID_INPUT_LENGTH)); - EXPECT_THAT(jsonRpc->Invoke(0, 0, "", "deleteKey", "{\"namespace\":\"x\"}", response), Eq(Core::ERROR_INVALID_INPUT_LENGTH)); -} - -TEST_F(AJsonRpc, DoesNotDeleteNamespaceWhenMandatoryParamsAreMissing) -{ - string response; - EXPECT_THAT(jsonRpc->Invoke(0, 0, "", "deleteNamespace", "", response), Eq(Core::ERROR_INVALID_INPUT_LENGTH)); -} - -TEST_F(AJsonRpc, FlushesCacheWithoutError) -{ - string response; - EXPECT_THAT(jsonRpc->Invoke(0, 0, "", "flushCache", "", response), Eq(Core::ERROR_NONE)); - EXPECT_THAT(response, Eq("{\"success\":true}")); -} - -TEST_F(AJsonRpc, GetsDefaultLimitForExistingNamespace) -{ - string response; - ASSERT_THAT(jsonRpc->Invoke(0, 0, "", "setValue", "{\"namespace\":\"ns1\",\"key\":\"key1\",\"value\":\"value1\"}", response), Eq(Core::ERROR_NONE)); - ASSERT_THAT(response, Eq("{\"success\":true}")); - // Namespace added - - EXPECT_THAT(jsonRpc->Invoke(0, 0, "", "getNamespaceStorageLimit", "{\"namespace\":\"ns1\"}", response), Eq(Core::ERROR_NONE)); - EXPECT_THAT(response, Eq("{\"storageLimit\":50}")); -} - -TEST_F(AJsonRpc, GetsLimitThatWasSet) -{ - string response; - EXPECT_THAT(jsonRpc->Invoke(0, 0, "", "setNamespaceStorageLimit", "{\"namespace\":\"ns1\",\"storageLimit\":\"10\"}", response), Eq(Core::ERROR_NONE)); - // Namespace limit set - - EXPECT_THAT(jsonRpc->Invoke(0, 0, "", "getNamespaceStorageLimit", "{\"namespace\":\"ns1\"}", response), Eq(Core::ERROR_NONE)); - EXPECT_THAT(response, Eq("{\"storageLimit\":10}")); -} - -class AJsonRpcWithValues : public AJsonRpc { -protected: - ~AJsonRpcWithValues() override = default; - void SetUp() override - { - AJsonRpc::SetUp(); - string response; - ASSERT_THAT(jsonRpc->Invoke(0, 0, "", "setValue", "{\"namespace\":\"ns1\",\"key\":\"key1\",\"value\":\"value1\"}", response), Eq(Core::ERROR_NONE)); - ASSERT_THAT(response, Eq("{\"success\":true}")); - ASSERT_THAT(jsonRpc->Invoke(0, 0, "", "setValue", "{\"namespace\":\"ns1\",\"key\":\"key2\",\"value\":\"value2\"}", response), Eq(Core::ERROR_NONE)); - ASSERT_THAT(response, Eq("{\"success\":true}")); - ASSERT_THAT(jsonRpc->Invoke(0, 0, "", "setValue", "{\"namespace\":\"ns2\",\"key\":\"key1\",\"value\":\"value1\"}", response), Eq(Core::ERROR_NONE)); - ASSERT_THAT(response, Eq("{\"success\":true}")); - } -}; - -TEST_F(AJsonRpcWithValues, GetsValues) -{ - string response; - EXPECT_THAT(jsonRpc->Invoke(0, 0, "", "getValue", "{\"namespace\":\"ns1\",\"key\":\"key1\"}", response), Eq(Core::ERROR_NONE)); - EXPECT_THAT(response, Eq("{\"value\":\"value1\",\"success\":true}")); - EXPECT_THAT(jsonRpc->Invoke(0, 0, "", "getValue", "{\"namespace\":\"ns1\",\"key\":\"key2\"}", response), Eq(Core::ERROR_NONE)); - EXPECT_THAT(response, Eq("{\"value\":\"value2\",\"success\":true}")); - EXPECT_THAT(jsonRpc->Invoke(0, 0, "", "getValue", "{\"namespace\":\"ns2\",\"key\":\"key1\"}", response), Eq(Core::ERROR_NONE)); - EXPECT_THAT(response, Eq("{\"value\":\"value1\",\"success\":true}")); -} - -TEST_F(AJsonRpcWithValues, DoesNotGetUnknownValueInExistingNamespace) -{ - string response; - EXPECT_THAT(jsonRpc->Invoke(0, 0, "", "getValue", "{\"namespace\":\"ns1\",\"key\":\"x\"}", response), Eq(Core::ERROR_UNKNOWN_KEY)); -} - -TEST_F(AJsonRpcWithValues, GetsKeys) -{ - string response; - EXPECT_THAT(jsonRpc->Invoke(0, 0, "", "getKeys", "{\"namespace\":\"ns1\"}", response), Eq(Core::ERROR_NONE)); - EXPECT_THAT(response, Eq("{\"keys\":[\"key1\",\"key2\"],\"success\":true}")); -} - -TEST_F(AJsonRpcWithValues, GetsNamespaces) -{ - string response; - EXPECT_THAT(jsonRpc->Invoke(0, 0, "", "getNamespaces", "", response), Eq(Core::ERROR_NONE)); - EXPECT_THAT(response, Eq("{\"namespaces\":[\"ns1\",\"ns2\"],\"success\":true}")); -} - -TEST_F(AJsonRpcWithValues, GetsStorageSizes) -{ - string response; - EXPECT_THAT(jsonRpc->Invoke(0, 0, "", "getStorageSizes", "", response), Eq(Core::ERROR_NONE)); - EXPECT_THAT(response, Eq("{\"storageList\":[{\"namespace\":\"ns1\",\"size\":20},{\"namespace\":\"ns2\",\"size\":10}]}")); -} - -// Deprecated -TEST_F(AJsonRpcWithValues, GetsStorageSize) -{ - string response; - EXPECT_THAT(jsonRpc->Invoke(0, 0, "", "getStorageSize", "", response), Eq(Core::ERROR_NONE)); - EXPECT_THAT(response, Eq("{\"namespaceSizes\":{\"ns1\":20,\"ns2\":10},\"success\":true}")); -} - -TEST_F(AJsonRpcWithValues, DoesNotGetDeletedValue) -{ - string response; - EXPECT_THAT(jsonRpc->Invoke(0, 0, "", "deleteKey", "{\"namespace\":\"ns1\",\"key\":\"key1\"}", response), Eq(Core::ERROR_NONE)); - EXPECT_THAT(response, Eq("{\"success\":true}")); - // Value is deleted - - EXPECT_THAT(jsonRpc->Invoke(0, 0, "", "getValue", "{\"namespace\":\"ns1\",\"key\":\"key1\"}", response), Eq(Core::ERROR_UNKNOWN_KEY)); -} - -TEST_F(AJsonRpcWithValues, DoesNotGetValueInDeletedNamespace) -{ - string response; - EXPECT_THAT(jsonRpc->Invoke(0, 0, "", "deleteNamespace", "{\"namespace\":\"ns1\"}", response), Eq(Core::ERROR_NONE)); - EXPECT_THAT(response, Eq("{\"success\":true}")); - // Namespace is deleted - - EXPECT_THAT(jsonRpc->Invoke(0, 0, "", "getValue", "{\"namespace\":\"ns1\",\"key\":\"key1\"}", response), Eq(Core::ERROR_NOT_EXIST)); -} diff --git a/PersistentStore/l1test/PluginTest.cpp b/PersistentStore/l1test/PluginTest.cpp deleted file mode 100644 index 11cd232bf6..0000000000 --- a/PersistentStore/l1test/PluginTest.cpp +++ /dev/null @@ -1,121 +0,0 @@ -#include -#include - -#include "../PersistentStore.h" - -#include "ServiceMock.h" - -using namespace WPEFramework; -using namespace WPEFramework::Plugin; - -using ::testing::Eq; -using ::testing::IsFalse; -using ::testing::IsTrue; -using ::testing::NiceMock; -using ::testing::NotNull; -using ::testing::Test; - -const std::string Path = "/tmp/persistentstore/l1test/plugintest"; -const std::string Path2 = "/tmp/persistentstore/l1test/plugintest2"; -const uint32_t MaxSize = 100; -const uint32_t MaxValue = 10; -const uint32_t Limit = 50; - -class APlugin : public Test { -protected: - NiceMock service; - PluginHost::IPlugin* plugin; - ~APlugin() override = default; - void SetUp() override - { - Core::File(Path).Destroy(); - // File is destroyed - - plugin = Core::Service::Create(); - JsonObject config; - config["path"] = Path; - config["maxsize"] = MaxSize; - config["maxvalue"] = MaxValue; - config["limit"] = Limit; - string configJsonStr; - config.ToString(configJsonStr); - ON_CALL(service, ConfigLine()) - .WillByDefault( - ::testing::Return(configJsonStr)); - ASSERT_THAT(plugin->Initialize(&service), Eq("")); - } - void TearDown() override - { - plugin->Deinitialize(&service); - plugin->Release(); - } -}; - -TEST_F(APlugin, CreatesFile) -{ - EXPECT_THAT(Core::File(Path).Exists(), IsTrue()); -} - -template -class APluginWithValue : public ACTUALPLUGINTEST { -protected: - ~APluginWithValue() override = default; - void SetUp() override - { - ACTUALPLUGINTEST::SetUp(); - auto jsonRpc = this->plugin->template QueryInterface(); - ASSERT_THAT(jsonRpc, NotNull()); - string response; - ASSERT_THAT(jsonRpc->Invoke(0, 0, "", "setValue", "{\"namespace\":\"ns1\",\"key\":\"key1\",\"value\":\"value1\"}", response), Eq(Core::ERROR_NONE)); - ASSERT_THAT(response, Eq("{\"success\":true}")); - if (jsonRpc) - jsonRpc->Release(); - } -}; - -template -class APluginReopenedAtPath2 : public ACTUALPLUGINTEST { -protected: - ~APluginReopenedAtPath2() override = default; - void SetUp() override - { - ACTUALPLUGINTEST::SetUp(); - Core::File(Path2).Destroy(); - // File is destroyed - - this->plugin->Deinitialize(&this->service); - JsonObject config; - config["path"] = Path2; - config["legacypath"] = Path; - config["maxsize"] = MaxSize; - config["maxvalue"] = MaxValue; - config["limit"] = Limit; - string configJsonStr; - config.ToString(configJsonStr); - ON_CALL(this->service, ConfigLine()) - .WillByDefault( - ::testing::Return(configJsonStr)); - ASSERT_THAT(this->plugin->Initialize(&this->service), Eq("")); - // Reopened at Path2 - } -}; - -class APluginWithValueReopenedAtPath2 : public APluginReopenedAtPath2> { -}; - -TEST_F(APluginWithValueReopenedAtPath2, MovesFile) -{ - EXPECT_THAT(Core::File(Path2).Exists(), IsTrue()); - EXPECT_THAT(Core::File(Path).Exists(), IsFalse()); -} - -TEST_F(APluginWithValueReopenedAtPath2, GetsValue) -{ - string response; - auto jsonRpc = plugin->QueryInterface(); - ASSERT_THAT(jsonRpc, NotNull()); - EXPECT_THAT(jsonRpc->Invoke(0, 0, "", "getValue", "{\"namespace\":\"ns1\",\"key\":\"key1\"}", response), Eq(Core::ERROR_NONE)); - EXPECT_THAT(response, Eq("{\"value\":\"value1\",\"success\":true}")); - if (jsonRpc) - jsonRpc->Release(); -} diff --git a/PersistentStore/l1test/StoreTest.cpp b/PersistentStore/l1test/StoreTest.cpp deleted file mode 100644 index 3219d736f5..0000000000 --- a/PersistentStore/l1test/StoreTest.cpp +++ /dev/null @@ -1,149 +0,0 @@ -#include -#include - -#include "../PersistentStore.h" - -#include "ServiceMock.h" - -using namespace WPEFramework; -using namespace WPEFramework::Plugin; - -using ::testing::Eq; -using ::testing::NiceMock; -using ::testing::NotNull; -using ::testing::Test; - -const std::string Path = "/tmp/persistentstore/l1test/storetest"; -const uint32_t MaxSize = 100; -const uint32_t MaxValue = 10; -const uint32_t Limit = 50; - -class AStore : public Test { -protected: - NiceMock service; - PluginHost::IPlugin* plugin; - Exchange::IStore* store; - ~AStore() override = default; - void SetUp() override - { - Core::File(Path).Destroy(); - // File is destroyed - - plugin = Core::Service::Create(); - JsonObject config; - config["path"] = Path; - config["maxsize"] = MaxSize; - config["maxvalue"] = MaxValue; - config["limit"] = Limit; - string configJsonStr; - config.ToString(configJsonStr); - ON_CALL(service, ConfigLine()) - .WillByDefault( - ::testing::Return(configJsonStr)); - ASSERT_THAT(plugin->Initialize(&service), Eq("")); - store = plugin->QueryInterface(); - ASSERT_THAT(store, NotNull()); - } - void TearDown() override - { - if (store) - store->Release(); - plugin->Deinitialize(&service); - plugin->Release(); - } -}; - -class AStoreWithNotification : public AStore { -protected: - class StoreNotification : public Exchange::IStore::INotification { - public: - Core::Event _valueChanged; - string _ns; - string _key; - string _value; - StoreNotification() - : _valueChanged(false, true) - { - } - void ValueChanged(const string& ns, const string& key, const string& value) override - { - _valueChanged.SetEvent(); - _ns = ns; - _key = key; - _value = value; - } - void StorageExceeded() override - { - } - - BEGIN_INTERFACE_MAP(StoreNotification) - INTERFACE_ENTRY(Exchange::IStore::INotification) - END_INTERFACE_MAP - }; - Core::Sink sink; - ~AStoreWithNotification() override = default; - void SetUp() override - { - AStore::SetUp(); - store->Register(&sink); - } - void TearDown() override - { - store->Unregister(&sink); - AStore::TearDown(); - } -}; - -TEST_F(AStoreWithNotification, TriggersNotificationWhenValueIsSet) -{ - ASSERT_THAT(store->SetValue("ns1", "key1", "value1"), Eq(Core::ERROR_NONE)); - // Value is set - - EXPECT_THAT(sink._valueChanged.Lock(100), Eq(Core::ERROR_NONE)); - EXPECT_THAT(sink._ns, Eq("ns1")); - EXPECT_THAT(sink._key, Eq("key1")); - EXPECT_THAT(sink._value, Eq("value1")); -} - -class AStoreWithValue : public AStore { -protected: - ~AStoreWithValue() override = default; - void SetUp() override - { - AStore::SetUp(); - ASSERT_THAT(store->SetValue("ns1", "key1", "value1"), Eq(Core::ERROR_NONE)); - } -}; - -TEST_F(AStoreWithValue, GetsValue) -{ - string value; - EXPECT_THAT(store->GetValue("ns1", "key1", value), Eq(Core::ERROR_NONE)); - EXPECT_THAT(value, Eq("value1")); -} - -TEST_F(AStoreWithValue, UpdatesValue) -{ - EXPECT_THAT(store->SetValue("ns1", "key1", "a"), Eq(Core::ERROR_NONE)); - string value; - EXPECT_THAT(store->GetValue("ns1", "key1", value), Eq(Core::ERROR_NONE)); - EXPECT_THAT(value, Eq("a")); -} - -TEST_F(AStoreWithValue, DoesNotGetDeletedValue) -{ - EXPECT_THAT(store->DeleteKey("ns1", "key1"), Eq(Core::ERROR_NONE)); - // Value is deleted - - string value; - EXPECT_THAT(store->GetValue("ns1", "key1", value), Eq(Core::ERROR_UNKNOWN_KEY)); -} - -TEST_F(AStoreWithValue, DoesNotGetValueInDeletedNamespace) -{ - EXPECT_THAT(store->DeleteNamespace("ns1"), Eq(Core::ERROR_NONE)); - // Namespace is deleted - - string value; - EXPECT_THAT(store->GetValue("ns1", "key1", value), Eq(Core::ERROR_NOT_EXIST)); -} diff --git a/PersistentStore/sqlite/Handle.cpp b/PersistentStore/sqlite/Handle.cpp deleted file mode 100644 index 54229e09e6..0000000000 --- a/PersistentStore/sqlite/Handle.cpp +++ /dev/null @@ -1,184 +0,0 @@ -/* - * If not stated otherwise in this file or this component's LICENSE file the - * following copyright and licenses apply: - * - * Copyright 2022 RDK Management - * - * 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. - */ - -#include "Handle.h" - -namespace WPEFramework { -namespace Plugin { - namespace Sqlite { - - uint32_t Handle::Open() - { - Core::SafeSyncType lock(_dataLock); - - Close(); - - uint32_t result; - - string path; - Core::SystemInfo::GetEnvironment(_T("PERSISTENTSTORE_PATH"), path); - - Core::File file(path); - Core::Directory(file.PathName().c_str()).CreatePath(); - - auto rc = sqlite3_open(path.c_str(), &_data); - if (rc == SQLITE_OK) { - result = CreateSchema(); - } else { - TRACE(Trace::Error, (_T("Sqlite open error %d for path %s"), rc, path.c_str())); - result = Core::ERROR_GENERAL; - } - - return result; - } - - uint32_t Handle::Close() - { - Core::SafeSyncType lock(_dataLock); - - uint32_t result; - - if (!_data) { - // Seems closed! - result = Core::ERROR_NONE; - } else { - auto rc = sqlite3_db_cacheflush(_data); - if (rc != SQLITE_OK) { - TRACE(Trace::Error, (_T("Sqlite cache flush error %d"), rc)); - } - - rc = sqlite3_close_v2(_data); - if (rc == SQLITE_OK) { - result = Core::ERROR_NONE; - } else { - TRACE(Trace::Error, (_T("Sqlite close error %d"), rc)); - result = Core::ERROR_GENERAL; - } - } - - return result; - } - - uint32_t Handle::CreateSchema() - { - Core::SafeSyncType lock(_dataLock); - - uint32_t result; - - string maxValue, maxSize; - Core::SystemInfo::GetEnvironment(_T("PERSISTENTSTORE_MAXVALUE"), maxValue); - Core::SystemInfo::GetEnvironment(_T("PERSISTENTSTORE_MAXSIZE"), maxSize); - - // This may be called for an existing database too, note the "if not exists" - - const std::vector statements = { - "CREATE TABLE if not exists namespace (" - "id INTEGER PRIMARY KEY," - "name TEXT UNIQUE" - ");", - "CREATE TABLE if not exists item (" - "ns INTEGER," - "key TEXT," - "value TEXT," - "FOREIGN KEY(ns) REFERENCES namespace(id) ON DELETE CASCADE ON UPDATE NO ACTION," - "UNIQUE(ns,key) ON CONFLICT REPLACE" - ");", - "CREATE TABLE if not exists limits (" - "n INTEGER," - "size INTEGER," - "FOREIGN KEY(n) REFERENCES namespace(id) ON DELETE CASCADE ON UPDATE NO ACTION," - "UNIQUE(n) ON CONFLICT REPLACE" - ");", - "PRAGMA foreign_keys = ON;", - "PRAGMA busy_timeout = 1000000;", - "CREATE TEMPORARY TRIGGER if not exists namespacetrigger INSERT ON namespace" - " BEGIN" - " SELECT CASE" - " WHEN length(new.name) = 0" - " THEN RAISE (FAIL, 'name empty')" - " WHEN length(new.name) > " - + maxValue + " THEN RAISE (FAIL, 'name too large')" - " WHEN (SELECT sum(s) FROM (SELECT sum(length(key)+length(value)) s FROM item" - " UNION ALL" - " SELECT sum(length(name)) s FROM namespace" - " UNION ALL" - " SELECT length(new.name) s)) > " - + maxSize + " THEN RAISE (FAIL, 'db full')" - " END;" - "END;", - "CREATE TEMPORARY TRIGGER if not exists itemtrigger INSERT ON item" - " BEGIN" - " SELECT CASE" - " WHEN length(new.key) = 0" - " THEN RAISE (FAIL, 'key empty')" - " WHEN length(new.key) > " - + maxValue + " THEN RAISE (FAIL, 'key too large')" - " WHEN length(new.value) > " - + maxValue + " THEN RAISE (FAIL, 'value too large')" - " WHEN (SELECT sum(s) FROM (SELECT sum(length(key)+length(value)) s FROM item" - " UNION ALL" - " SELECT sum(length(name)) s FROM namespace" - " UNION ALL" - " SELECT length(new.key)+length(new.value) s)) > " - + maxSize + " THEN RAISE (FAIL, 'db full')" - " WHEN (SELECT size-length(new.key)-length(new.value)-sum(length(key)+length(value)) FROM limits" - " INNER JOIN item ON limits.n = item.ns" - " where n = new.ns) < 0" - " THEN RAISE (FAIL, 'limit')" - " END;" - "END;" - }; - - for (auto& sql : statements) { - char* errmsg = nullptr; - auto rc = sqlite3_exec(_data, sql.c_str(), 0, 0, &errmsg); - - if ((rc == SQLITE_OK) && (errmsg == nullptr)) { - result = Core::ERROR_NONE; - } else { - TRACE(Trace::Error, (_T("Sqlite '%s' error %d"), sql.c_str(), rc)); - if (errmsg) { - TRACE(Trace::Error, (_T("Sqlite error '%s'"), errmsg)); - sqlite3_free(errmsg); - } - if (rc == SQLITE_NOTADB) { - result = Core::ERROR_UNAVAILABLE; - } else { - result = Core::ERROR_GENERAL; - } - break; - } - } - - if (result == Core::ERROR_NONE) { - auto rc = sqlite3_exec(_data, "ALTER TABLE item ADD COLUMN ttl INTEGER;", 0, 0, 0); - if (rc == SQLITE_NOTADB) { - TRACE(Trace::Error, (_T("Sqlite alter table error %d"), rc)); - result = Core::ERROR_UNAVAILABLE; - } else { - // If the column exists, we are not interested in this error - } - } - - return result; - } - - } // namespace Sqlite -} // namespace Plugin -} // namespace WPEFramework diff --git a/PersistentStore/sqlite/Handle.h b/PersistentStore/sqlite/Handle.h deleted file mode 100644 index 6d95810fe6..0000000000 --- a/PersistentStore/sqlite/Handle.h +++ /dev/null @@ -1,60 +0,0 @@ -/* - * If not stated otherwise in this file or this component's LICENSE file the - * following copyright and licenses apply: - * - * Copyright 2022 RDK Management - * - * 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 "../Module.h" - -#include - -namespace WPEFramework { -namespace Plugin { - namespace Sqlite { - - class Handle { - private: - Handle(const Handle&) = delete; - Handle& operator=(const Handle&) = delete; - - public: - Handle() - : _data(nullptr) - { - Open(); - } - virtual ~Handle() { Close(); } - - virtual uint32_t Open(); - inline operator sqlite3*() - { - return (_data); - } - - private: - uint32_t Close(); - uint32_t CreateSchema(); - - private: - sqlite3* _data; - Core::CriticalSection _dataLock; - }; - - } // namespace Sqlite -} // namespace Plugin -} // namespace WPEFramework diff --git a/PersistentStore/sqlite/Store2.cpp b/PersistentStore/sqlite/Store2.cpp new file mode 100644 index 0000000000..90e350d106 --- /dev/null +++ b/PersistentStore/sqlite/Store2.cpp @@ -0,0 +1,10 @@ +#include "Store2.h" + +namespace WPEFramework { +namespace Plugin { + namespace Sqlite { + using SqliteStore2 = Store2; + SERVICE_REGISTRATION(SqliteStore2, 1, 0); + } // namespace Sqlite +} // namespace Plugin +} // namespace WPEFramework diff --git a/PersistentStore/sqlite/Store2.h b/PersistentStore/sqlite/Store2.h new file mode 100644 index 0000000000..a3e861562f --- /dev/null +++ b/PersistentStore/sqlite/Store2.h @@ -0,0 +1,321 @@ +/* + * If not stated otherwise in this file or this component's LICENSE file the + * following copyright and licenses apply: + * + * Copyright 2022 RDK Management + * + * 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 "../Module.h" +#include +#include + +namespace WPEFramework { +namespace Plugin { + namespace Sqlite { + + class Store2 : public Exchange::IStore2 { + private: + Store2(const Store2&) = delete; + Store2& operator=(const Store2&) = delete; + + public: + Store2() + : Store2( + getenv(PATH_ENV), + std::stoul(getenv(MAXSIZE_ENV)), + std::stoul(getenv(MAXVALUE_ENV)), + std::stoul(getenv(LIMIT_ENV))) + { + } + Store2(const string& path, const uint64_t maxSize, const uint64_t maxValue, const uint64_t limit) + : IStore2() + , _path(path) + , _maxSize(maxSize) + , _maxValue(maxValue) + , _limit(limit) + { + Open(); + } + ~Store2() override + { + Close(); + } + + private: + void Open() + { + Core::File file(_path); + Core::Directory(file.PathName().c_str()).CreatePath(); + auto rc = sqlite3_open(_path.c_str(), &_data); + if (rc != SQLITE_OK) { + OnError(__FUNCTION__, rc); + } + const std::vector statements = { + "pragma foreign_keys = on;", + "pragma busy_timeout = 1000000;", + "create table if not exists namespace (id integer primary key,name text unique);", + "create table if not exists item (ns integer,key text,value text,foreign key(ns) references namespace(id) on delete cascade on update no action,unique(ns,key) on conflict replace);", + "create table if not exists limits (n integer,size integer,foreign key(n) references namespace(id) on delete cascade on update no action,unique(n) on conflict replace);", + "alter table item add column ttl integer;", + "create temporary trigger if not exists ns_empty insert on namespace begin select case when length(new.name) = 0 then raise (fail, 'empty') end; end;", + "create temporary trigger if not exists key_empty insert on item begin select case when length(new.key) = 0 then raise (fail, 'empty') end; end;", + "create temporary trigger if not exists ns_maxvalue insert on namespace begin select case when length(new.name) > " + std::to_string(_maxValue) + " then raise (fail, 'max value') end; end;", + "create temporary trigger if not exists key_maxvalue insert on item begin select case when length(new.key) > " + std::to_string(_maxValue) + " then raise (fail, 'max value') end; end;", + "create temporary trigger if not exists value_maxvalue insert on item begin select case when length(new.value) > " + std::to_string(_maxValue) + " then raise (fail, 'max value') end; end;", + "create temporary trigger if not exists ns_maxsize insert on namespace begin select case when (select sum(s) from (select sum(length(key)+length(value)) s from item union all select sum(length(name)) s from namespace union all select length(new.name) s)) > " + std::to_string(_maxSize) + " then raise (fail, 'max size') end; end;", + "create temporary trigger if not exists item_maxsize insert on item begin select case when (select sum(s) from (select sum(length(key)+length(value)) s from item union all select sum(length(name)) s from namespace union all select length(new.key)+length(new.value) s)) > " + std::to_string(_maxSize) + " then raise (fail, 'max size') end; end;", + "create temporary trigger if not exists item_limit_default insert on item begin select case when (select length(new.key)+length(new.value)+sum(length(key)+length(value)) from item where ns = new.ns) > " + std::to_string(_limit) + " then raise (fail, 'limit') end; end;", + "create temporary trigger if not exists item_limit insert on item begin select case when (select size-length(new.key)-length(new.value)-sum(length(key)+length(value)) from limits inner join item on limits.n = item.ns where n = new.ns) < 0 then raise (fail, 'limit') end; end;" + }; + for (auto& sql : statements) { + auto rc = sqlite3_exec(_data, sql.c_str(), nullptr, nullptr, nullptr); + if (rc != SQLITE_OK) { + OnError(__FUNCTION__, rc); + } + } + } + void Close() + { + auto rc = sqlite3_close_v2(_data); + if (rc != SQLITE_OK) { + OnError(__FUNCTION__, rc); + } + } + + public: + uint32_t Register(INotification* notification) override + { + Core::SafeSyncType lock(_clientLock); + + ASSERT(std::find(_clients.begin(), _clients.end(), notification) == _clients.end()); + + notification->AddRef(); + _clients.push_back(notification); + + return Core::ERROR_NONE; + } + uint32_t Unregister(INotification* notification) override + { + Core::SafeSyncType lock(_clientLock); + + std::list::iterator + index(std::find(_clients.begin(), _clients.end(), notification)); + + ASSERT(index != _clients.end()); + + if (index != _clients.end()) { + notification->Release(); + _clients.erase(index); + } + + return Core::ERROR_NONE; + } + + uint32_t SetValue(const ScopeType scope, const string& ns, const string& key, const string& value, const uint32_t ttl) override + { + ASSERT(scope == ScopeType::DEVICE); + + uint32_t result; + + int64_t t = 0; + if (ttl != 0) { + if (getenv(CLOCKSYNC_ENV) != nullptr) { + int64_t now = Core::Time::Now().Ticks() / Core::Time::TicksPerMillisecond / 1000; + t = ttl + now; + } else { + return Core::ERROR_PENDING_CONDITIONS; + } + } + sqlite3_stmt* stmt; + sqlite3_prepare_v2(_data, "insert or ignore into namespace (name) values (?);", + -1, &stmt, nullptr); + sqlite3_bind_text(stmt, 1, ns.c_str(), -1, SQLITE_TRANSIENT); + auto rc = sqlite3_step(stmt); + sqlite3_finalize(stmt); + if (rc == SQLITE_DONE) { + sqlite3_prepare_v2(_data, "insert into item (ns,key,value,ttl)" + " select id, ?, ?, ?" + " from namespace" + " where name = ?" + ";", + -1, &stmt, nullptr); + sqlite3_bind_text(stmt, 1, key.c_str(), -1, SQLITE_TRANSIENT); + sqlite3_bind_text(stmt, 2, value.c_str(), -1, SQLITE_TRANSIENT); + if (t != 0) { + sqlite3_bind_int64(stmt, 3, t); + } else { + sqlite3_bind_null(stmt, 3); + } + sqlite3_bind_text(stmt, 4, ns.c_str(), -1, SQLITE_TRANSIENT); + rc = sqlite3_step(stmt); + sqlite3_finalize(stmt); + } + + if (rc == SQLITE_DONE) { + OnValueChanged(ns, key, value); + result = Core::ERROR_NONE; + } else { + OnError(__FUNCTION__, rc); + if (rc == SQLITE_CONSTRAINT) { + result = Core::ERROR_INVALID_INPUT_LENGTH; + } else { + result = Core::ERROR_GENERAL; + } + } + + return result; + } + uint32_t GetValue(const ScopeType scope, const string& ns, const string& key, string& value, uint32_t& ttl) override + { + ASSERT(scope == ScopeType::DEVICE); + + uint32_t result; + + string k, v; + int64_t t = 0; + sqlite3_stmt* stmt; + sqlite3_prepare_v2(_data, "select key, value, ttl" + " from namespace" + " left join item on (namespace.id = item.ns and key = ?)" + " where name = ?" + ";", + -1, &stmt, nullptr); + sqlite3_bind_text(stmt, 1, key.c_str(), -1, SQLITE_TRANSIENT); + sqlite3_bind_text(stmt, 2, ns.c_str(), -1, SQLITE_TRANSIENT); + auto rc = sqlite3_step(stmt); + if (rc == SQLITE_ROW) { + if (sqlite3_column_type(stmt, 0) != SQLITE_NULL) { + k = (const char*)sqlite3_column_text(stmt, 0); + v = (const char*)sqlite3_column_text(stmt, 1); + t = sqlite3_column_int64(stmt, 2); + } + } + sqlite3_finalize(stmt); + + if (rc == SQLITE_ROW) { + if (!k.empty()) { + if (t == 0) { + value = v; + ttl = 0; + result = Core::ERROR_NONE; + } else if (getenv(CLOCKSYNC_ENV) != nullptr) { + int64_t now = Core::Time::Now().Ticks() / Core::Time::TicksPerMillisecond / 1000; + if (t > now) { + value = v; + ttl = t - now; + result = Core::ERROR_NONE; + } else { + result = Core::ERROR_UNKNOWN_KEY; + } + } else { + result = Core::ERROR_PENDING_CONDITIONS; + } + } else { + result = Core::ERROR_UNKNOWN_KEY; + } + } else if (rc == SQLITE_DONE) { + result = Core::ERROR_NOT_EXIST; + } else { + OnError(__FUNCTION__, rc); + result = Core::ERROR_GENERAL; + } + + return result; + } + uint32_t DeleteKey(const ScopeType scope, const string& ns, const string& key) override + { + ASSERT(scope == ScopeType::DEVICE); + + uint32_t result; + + sqlite3_stmt* stmt; + sqlite3_prepare_v2(_data, "delete from item" + " where ns in (select id from namespace where name = ?)" + " and key = ?" + ";", + -1, &stmt, nullptr); + sqlite3_bind_text(stmt, 1, ns.c_str(), -1, SQLITE_TRANSIENT); + sqlite3_bind_text(stmt, 2, key.c_str(), -1, SQLITE_TRANSIENT); + auto rc = sqlite3_step(stmt); + sqlite3_finalize(stmt); + + if (rc == SQLITE_DONE) { + result = Core::ERROR_NONE; + } else { + OnError(__FUNCTION__, rc); + result = Core::ERROR_GENERAL; + } + + return result; + } + uint32_t DeleteNamespace(const ScopeType scope, const string& ns) override + { + ASSERT(scope == ScopeType::DEVICE); + + uint32_t result; + + sqlite3_stmt* stmt; + sqlite3_prepare_v2(_data, "delete from namespace where name = ?;", -1, &stmt, nullptr); + sqlite3_bind_text(stmt, 1, ns.c_str(), -1, SQLITE_TRANSIENT); + auto rc = sqlite3_step(stmt); + sqlite3_finalize(stmt); + + if (rc == SQLITE_DONE) { + result = Core::ERROR_NONE; + } else { + OnError(__FUNCTION__, rc); + result = Core::ERROR_GENERAL; + } + + return result; + } + + BEGIN_INTERFACE_MAP(Store2) + INTERFACE_ENTRY(IStore2) + END_INTERFACE_MAP + + private: + void OnValueChanged(const string& ns, const string& key, const string& value) + { + Core::SafeSyncType lock(_clientLock); + + std::list::iterator + index(_clients.begin()); + + while (index != _clients.end()) { + (*index)->ValueChanged(ScopeType::DEVICE, ns, key, value); + index++; + } + } + void OnError(const char* fn, const int status) const + { + TRACE(Trace::Error, (_T("%s sqlite error %d"), status)); + } + + private: + const string _path; + const uint64_t _maxSize; + const uint64_t _maxValue; + const uint64_t _limit; + sqlite3* _data; + std::list _clients; + Core::CriticalSection _clientLock; + }; + + } // namespace Sqlite +} // namespace Plugin +} // namespace WPEFramework diff --git a/PersistentStore/sqlite/Store2Type.h b/PersistentStore/sqlite/Store2Type.h deleted file mode 100644 index 2a3259d8b6..0000000000 --- a/PersistentStore/sqlite/Store2Type.h +++ /dev/null @@ -1,288 +0,0 @@ -/* - * If not stated otherwise in this file or this component's LICENSE file the - * following copyright and licenses apply: - * - * Copyright 2022 RDK Management - * - * 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 - -namespace WPEFramework { -namespace Plugin { - namespace Sqlite { - - template - class Store2Type : public Exchange::IStore2, protected ACTUALHANDLE { - private: - Store2Type(const Store2Type&) = delete; - Store2Type& operator=(const Store2Type&) = delete; - - public: - template - Store2Type(Args&&... args) - : Exchange::IStore2() - , ACTUALHANDLE(std::forward(args)...) - , _clients() - , _clientLock() - { - } - ~Store2Type() override = default; - - public: - // IStore2 methods - - uint32_t Register(Exchange::IStore2::INotification* notification) override - { - Core::SafeSyncType lock(_clientLock); - - ASSERT(std::find(_clients.begin(), _clients.end(), notification) == _clients.end()); - - notification->AddRef(); - _clients.push_back(notification); - - return Core::ERROR_NONE; - } - uint32_t Unregister(Exchange::IStore2::INotification* notification) override - { - Core::SafeSyncType lock(_clientLock); - - std::list::iterator - index(std::find(_clients.begin(), _clients.end(), notification)); - - ASSERT(index != _clients.end()); - - if (index != _clients.end()) { - notification->Release(); - _clients.erase(index); - } - - return Core::ERROR_NONE; - } - - uint32_t SetValue(const ScopeType scope, const string& ns, const string& key, const string& value, const uint32_t ttl) override - { - ASSERT(scope == ScopeType::DEVICE); - - uint32_t result; - - sqlite3_stmt* stmt; - sqlite3_prepare_v2(*this, "INSERT OR IGNORE INTO namespace (name) values (?);", - -1, &stmt, nullptr); - - sqlite3_bind_text(stmt, 1, ns.c_str(), -1, SQLITE_TRANSIENT); - - auto rc = sqlite3_step(stmt); - sqlite3_finalize(stmt); - - if (rc == SQLITE_DONE) { - sqlite3_prepare_v2(*this, "INSERT OR IGNORE INTO limits (n,size)" - " SELECT id, ?" - " FROM namespace" - " WHERE name = ?" - ";", - -1, &stmt, nullptr); - - string limit; - Core::SystemInfo::GetEnvironment(_T("PERSISTENTSTORE_LIMIT"), limit); - - sqlite3_bind_int(stmt, 1, std::stoul(limit)); - sqlite3_bind_text(stmt, 2, ns.c_str(), -1, SQLITE_TRANSIENT); - - rc = sqlite3_step(stmt); - sqlite3_finalize(stmt); - - if (rc == SQLITE_DONE) { - sqlite3_prepare_v2(*this, "INSERT INTO item (ns,key,value,ttl)" - " SELECT id, ?, ?, ?" - " FROM namespace" - " WHERE name = ?" - ";", - -1, &stmt, nullptr); - - sqlite3_bind_text(stmt, 1, key.c_str(), -1, SQLITE_TRANSIENT); - sqlite3_bind_text(stmt, 2, value.c_str(), -1, SQLITE_TRANSIENT); - if (ttl != 0) { - sqlite3_bind_int64(stmt, 3, Core::Time::Now().Ticks() / Core::Time::TicksPerMillisecond / 1000 + ttl); - } else { - sqlite3_bind_null(stmt, 3); - } - sqlite3_bind_text(stmt, 4, ns.c_str(), -1, SQLITE_TRANSIENT); - - rc = sqlite3_step(stmt); - sqlite3_finalize(stmt); - - if (rc == SQLITE_DONE) { - OnValueChanged(ns, key, value); - result = Core::ERROR_NONE; - } else if (rc == SQLITE_CONSTRAINT) { - result = Core::ERROR_INVALID_INPUT_LENGTH; - } else { - TRACE(Trace::Error, (_T("Sqlite insert item error %d"), rc)); - if (rc == SQLITE_READONLY || rc == SQLITE_CORRUPT) { - result = Core::ERROR_UNAVAILABLE; - } else { - result = Core::ERROR_GENERAL; - } - } - } else { - TRACE(Trace::Error, (_T("Sqlite insert limit error %d"), rc)); - if (rc == SQLITE_READONLY || rc == SQLITE_CORRUPT) { - result = Core::ERROR_UNAVAILABLE; - } else { - result = Core::ERROR_GENERAL; - } - } - } else if (rc == SQLITE_CONSTRAINT) { - result = Core::ERROR_INVALID_INPUT_LENGTH; - } else { - TRACE(Trace::Error, (_T("Sqlite insert namespace error %d"), rc)); - if (rc == SQLITE_READONLY || rc == SQLITE_CORRUPT) { - result = Core::ERROR_UNAVAILABLE; - } else { - result = Core::ERROR_GENERAL; - } - } - - return result; - } - uint32_t GetValue(const ScopeType scope, const string& ns, const string& key, string& value, uint32_t& ttl) override - { - ASSERT(scope == ScopeType::DEVICE); - - uint32_t result; - - sqlite3_stmt* stmt; - sqlite3_prepare_v2(*this, "SELECT key, value, ttl" - " FROM namespace" - " LEFT JOIN item ON (namespace.id = item.ns AND key = ?)" - " where name = ?" - ";", - -1, &stmt, nullptr); - - sqlite3_bind_text(stmt, 1, key.c_str(), -1, SQLITE_TRANSIENT); - sqlite3_bind_text(stmt, 2, ns.c_str(), -1, SQLITE_TRANSIENT); - - auto rc = sqlite3_step(stmt); - if (rc == SQLITE_ROW) { - if (sqlite3_column_type(stmt, 0) != SQLITE_NULL) { - if (sqlite3_column_type(stmt, 2) != SQLITE_NULL) { - auto seconds = (int64_t)sqlite3_column_int64(stmt, 2) - (int64_t)(Core::Time::Now().Ticks() / Core::Time::TicksPerMillisecond / 1000); - if (seconds > 0) { - value = (const char*)sqlite3_column_text(stmt, 1); - ttl = seconds; - result = Core::ERROR_NONE; - } else { - result = Core::ERROR_UNKNOWN_KEY; - } - } else { - value = (const char*)sqlite3_column_text(stmt, 1); - ttl = 0; - result = Core::ERROR_NONE; - } - } else { - result = Core::ERROR_UNKNOWN_KEY; - } - } else if (rc == SQLITE_DONE) { - result = Core::ERROR_NOT_EXIST; - } else { - TRACE(Trace::Error, (_T("Sqlite select item error %d"), rc)); - result = Core::ERROR_GENERAL; - } - - sqlite3_finalize(stmt); - - return result; - } - uint32_t DeleteKey(const ScopeType scope, const string& ns, const string& key) override - { - ASSERT(scope == ScopeType::DEVICE); - - uint32_t result; - - sqlite3_stmt* stmt; - sqlite3_prepare_v2(*this, "DELETE FROM item" - " where ns in (select id from namespace where name = ?)" - " and key = ?" - ";", - -1, &stmt, NULL); - - sqlite3_bind_text(stmt, 1, ns.c_str(), -1, SQLITE_TRANSIENT); - sqlite3_bind_text(stmt, 2, key.c_str(), -1, SQLITE_TRANSIENT); - - auto rc = sqlite3_step(stmt); - sqlite3_finalize(stmt); - - if (rc == SQLITE_DONE) { - result = Core::ERROR_NONE; - } else { - TRACE(Trace::Error, (_T("Sqlite delete item error %d"), rc)); - result = Core::ERROR_GENERAL; - } - - return result; - } - uint32_t DeleteNamespace(const ScopeType scope, const string& ns) override - { - ASSERT(scope == ScopeType::DEVICE); - - uint32_t result; - - sqlite3_stmt* stmt; - sqlite3_prepare_v2(*this, "DELETE FROM namespace where name = ?;", -1, &stmt, NULL); - - sqlite3_bind_text(stmt, 1, ns.c_str(), -1, SQLITE_TRANSIENT); - - auto rc = sqlite3_step(stmt); - sqlite3_finalize(stmt); - - if (rc == SQLITE_DONE) { - result = Core::ERROR_NONE; - } else { - TRACE(Trace::Error, (_T("Sqlite delete namespace error %d"), rc)); - result = Core::ERROR_GENERAL; - } - - return result; - } - - BEGIN_INTERFACE_MAP(Store2Type) - INTERFACE_ENTRY(Exchange::IStore2) - END_INTERFACE_MAP - - private: - void OnValueChanged(const string& ns, const string& key, const string& value) - { - Core::SafeSyncType lock(_clientLock); - - std::list::iterator - index(_clients.begin()); - - while (index != _clients.end()) { - (*index)->ValueChanged(ScopeType::DEVICE, ns, key, value); - index++; - } - } - - private: - std::list _clients; - Core::CriticalSection _clientLock; - }; - - } // namespace Sqlite -} // namespace Plugin -} // namespace WPEFramework diff --git a/PersistentStore/sqlite/Store2WithClockSyncType.h b/PersistentStore/sqlite/Store2WithClockSyncType.h deleted file mode 100644 index dfce976d95..0000000000 --- a/PersistentStore/sqlite/Store2WithClockSyncType.h +++ /dev/null @@ -1,134 +0,0 @@ -/* - * If not stated otherwise in this file or this component's LICENSE file the - * following copyright and licenses apply: - * - * Copyright 2022 RDK Management - * - * 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 - -namespace WPEFramework { -namespace Plugin { - namespace Sqlite { - - template - class Store2WithClockSyncType : public ACTUALSTORE2 { - private: - Store2WithClockSyncType(const Store2WithClockSyncType&) = delete; - Store2WithClockSyncType& operator=(const Store2WithClockSyncType&) = delete; - - struct ClockSync { - std::atomic_bool Synced{ false }; - }; - - using ClockSyncSingleton = Core::SingletonType; - - class IARMHandler { - private: - IARMHandler(const IARMHandler&) = delete; - IARMHandler& operator=(const IARMHandler&) = delete; - - public: - IARMHandler() - { - auto rc = IARM_Bus_Init("Thunder_Plugins"); - if ((rc != IARM_RESULT_SUCCESS) && (rc != IARM_RESULT_INVALID_STATE)) { - TRACE(Trace::Error, (_T("Sqlite IARM init error %d"), rc)); - } - rc = IARM_Bus_Connect(); - if ((rc != IARM_RESULT_SUCCESS) && (rc != IARM_RESULT_INVALID_STATE)) { - TRACE(Trace::Error, (_T("Sqlite IARM connect error %d"), rc)); - } - - IARM_Bus_SYSMgr_GetSystemStates_Param_t param; - memset(¶m, 0, sizeof(param)); - rc = IARM_Bus_Call(IARM_BUS_SYSMGR_NAME, IARM_BUS_SYSMGR_API_GetSystemStates, ¶m, sizeof(param)); - if (rc == IARM_RESULT_SUCCESS) { - TRACE(Trace::Information, (_T("Sqlite time source state is %d"), param.time_source.state)); - if (param.time_source.state) { - ClockSyncSingleton::Instance().Synced = true; - } - } else { - TRACE(Trace::Error, (_T("Sqlite IARM call error %d"), rc)); - } - - rc = IARM_Bus_RegisterEventHandler(IARM_BUS_SYSMGR_NAME, IARM_BUS_SYSMGR_EVENT_SYSTEMSTATE, Handler); - if (rc != IARM_RESULT_SUCCESS) { - TRACE(Trace::Error, (_T("Sqlite IARM register error %d"), rc)); - } - } - ~IARMHandler() - { - auto rc = IARM_Bus_RemoveEventHandler(IARM_BUS_SYSMGR_NAME, IARM_BUS_SYSMGR_EVENT_SYSTEMSTATE, Handler); - if (rc != IARM_RESULT_SUCCESS) { - TRACE(Trace::Error, (_T("Sqlite IARM remove error %d"), rc)); - } - } - - private: - static void Handler(const char* owner, IARM_EventId_t eventId, void* data, size_t len) - { - auto sysEventData = (IARM_Bus_SYSMgr_EventData_t*)data; - if (sysEventData->data.systemStates.stateId == IARM_BUS_SYSMGR_SYSSTATE_TIME_SOURCE) { - TRACE_GLOBAL(Trace::Information, (_T("Sqlite time source state changed %d"), sysEventData->data.systemStates.state)); - ClockSyncSingleton::Instance().Synced = sysEventData->data.systemStates.state; - } - } - }; - - public: - template - Store2WithClockSyncType(Args&&... args) - : ACTUALSTORE2(std::forward(args)...) - { - } - ~Store2WithClockSyncType() override = default; - - public: - uint32_t SetValue(const Exchange::IStore2::ScopeType scope, const string& ns, const string& key, const string& value, const uint32_t ttl) override - { - return ((ttl != 0) && !ClockSyncSingleton::Instance().Synced) - ? Core::ERROR_PENDING_CONDITIONS - : ACTUALSTORE2::SetValue(scope, ns, key, value, ttl); - } - uint32_t GetValue(const Exchange::IStore2::ScopeType scope, const string& ns, const string& key, string& value, uint32_t& ttl) override - { - string v; - uint32_t t; - auto result = ACTUALSTORE2::GetValue(scope, ns, key, v, t); - if (result == Core::ERROR_NONE) { - if ((t != 0) && !ClockSyncSingleton::Instance().Synced) { - result = Core::ERROR_PENDING_CONDITIONS; - } else { - value = v; - ttl = t; - } - } else if ((result == Core::ERROR_UNKNOWN_KEY) && !ClockSyncSingleton::Instance().Synced) { - result = Core::ERROR_PENDING_CONDITIONS; - } - return result; - } - - private: - IARMHandler handler; - }; - - } // namespace Sqlite -} // namespace Plugin -} // namespace WPEFramework diff --git a/PersistentStore/sqlite/Store2WithReconnectType.h b/PersistentStore/sqlite/Store2WithReconnectType.h deleted file mode 100644 index ce698b9876..0000000000 --- a/PersistentStore/sqlite/Store2WithReconnectType.h +++ /dev/null @@ -1,64 +0,0 @@ -/* - * If not stated otherwise in this file or this component's LICENSE file the - * following copyright and licenses apply: - * - * Copyright 2022 RDK Management - * - * 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 - -namespace WPEFramework { -namespace Plugin { - namespace Sqlite { - - // Note, this class expects sqlite error on write if the file deletes unexpectedly. As it's not documented it may not work - - template - class Store2WithReconnectType : public ACTUALSTORE2 { - private: - Store2WithReconnectType(const Store2WithReconnectType&) = delete; - Store2WithReconnectType& operator=(const Store2WithReconnectType&) = delete; - - public: - template - Store2WithReconnectType(Args&&... args) - : ACTUALSTORE2(std::forward(args)...) - { - } - ~Store2WithReconnectType() override = default; - - public: - uint32_t SetValue(const Exchange::IStore2::ScopeType scope, const string& ns, const string& key, const string& value, const uint32_t ttl) override - { - auto result = ACTUALSTORE2::SetValue(scope, ns, key, value, ttl); - if (result == Core::ERROR_UNAVAILABLE) { - // SetValue is so important to us that we want to create (presumably) a new file and write anyway - - if (ACTUALSTORE2::Open() == Core::ERROR_NONE) { - result = ACTUALSTORE2::SetValue(scope, ns, key, value, ttl); - } - } - if (result == Core::ERROR_UNAVAILABLE) { - result = Core::ERROR_GENERAL; - } - return result; - } - }; - - } // namespace Sqlite -} // namespace Plugin -} // namespace WPEFramework diff --git a/PersistentStore/sqlite/StoreCache.cpp b/PersistentStore/sqlite/StoreCache.cpp new file mode 100644 index 0000000000..b3f5fb1ba7 --- /dev/null +++ b/PersistentStore/sqlite/StoreCache.cpp @@ -0,0 +1,10 @@ +#include "StoreCache.h" + +namespace WPEFramework { +namespace Plugin { + namespace Sqlite { + using SqliteStoreCache = StoreCache; + SERVICE_REGISTRATION(SqliteStoreCache, 1, 0); + } // namespace Sqlite +} // namespace Plugin +} // namespace WPEFramework diff --git a/PersistentStore/sqlite/StoreCache.h b/PersistentStore/sqlite/StoreCache.h new file mode 100644 index 0000000000..7f108f1885 --- /dev/null +++ b/PersistentStore/sqlite/StoreCache.h @@ -0,0 +1,114 @@ +/* + * If not stated otherwise in this file or this component's LICENSE file the + * following copyright and licenses apply: + * + * Copyright 2022 RDK Management + * + * 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 "../Module.h" +#include +#include + +namespace WPEFramework { +namespace Plugin { + namespace Sqlite { + + class StoreCache : public Exchange::IStoreCache { + private: + StoreCache(const StoreCache&) = delete; + StoreCache& operator=(const StoreCache&) = delete; + + public: + StoreCache() + : StoreCache(getenv(PATH_ENV)) + { + } + StoreCache(const string& path) + : IStoreCache() + , _path(path) + { + Open(); + } + ~StoreCache() override + { + Close(); + } + + private: + void Open() + { + Core::File file(_path); + Core::Directory(file.PathName().c_str()).CreatePath(); + auto rc = sqlite3_open(_path.c_str(), &_data); + if (rc != SQLITE_OK) { + OnError(__FUNCTION__, rc); + } + const std::vector statements = { + "pragma busy_timeout = 1000000;" + }; + for (auto& sql : statements) { + auto rc = sqlite3_exec(_data, sql.c_str(), nullptr, nullptr, nullptr); + if (rc != SQLITE_OK) { + OnError(__FUNCTION__, rc); + } + } + } + void Close() + { + auto rc = sqlite3_close_v2(_data); + if (rc != SQLITE_OK) { + OnError(__FUNCTION__, rc); + } + } + + public: + uint32_t FlushCache() override + { + uint32_t result; + + auto rc = sqlite3_db_cacheflush(_data); + + if (rc == SQLITE_OK) { + result = Core::ERROR_NONE; + } else { + OnError(__FUNCTION__, rc); + result = Core::ERROR_GENERAL; + } + + sync(); + + return result; + } + + BEGIN_INTERFACE_MAP(StoreCache) + INTERFACE_ENTRY(IStoreCache) + END_INTERFACE_MAP + + private: + void OnError(const char* fn, const int status) const + { + TRACE(Trace::Error, (_T("%s sqlite error %d"), status)); + } + + private: + const string _path; + sqlite3* _data; + }; + + } // namespace Sqlite +} // namespace Plugin +} // namespace WPEFramework diff --git a/PersistentStore/sqlite/StoreCacheType.h b/PersistentStore/sqlite/StoreCacheType.h deleted file mode 100644 index 2c70848e25..0000000000 --- a/PersistentStore/sqlite/StoreCacheType.h +++ /dev/null @@ -1,72 +0,0 @@ -/* - * If not stated otherwise in this file or this component's LICENSE file the - * following copyright and licenses apply: - * - * Copyright 2022 RDK Management - * - * 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 - -namespace WPEFramework { -namespace Plugin { - namespace Sqlite { - - template - class StoreCacheType : public Exchange::IStoreCache, protected ACTUALHANDLE { - private: - StoreCacheType(const StoreCacheType&) = delete; - StoreCacheType& operator=(const StoreCacheType&) = delete; - - public: - template - StoreCacheType(Args&&... args) - : Exchange::IStoreCache() - , ACTUALHANDLE(std::forward(args)...) - { - } - ~StoreCacheType() override = default; - - public: - // IStoreCache methods - - uint32_t FlushCache() override - { - uint32_t result; - - auto rc = sqlite3_db_cacheflush(*this); - - if (rc == SQLITE_OK) { - result = Core::ERROR_NONE; - } else { - TRACE(Trace::Error, (_T("Sqlite cache flush error %d"), rc)); - result = Core::ERROR_GENERAL; - } - - sync(); - - return result; - } - - BEGIN_INTERFACE_MAP(StoreCacheType) - INTERFACE_ENTRY(Exchange::IStoreCache) - END_INTERFACE_MAP - }; - - } // namespace Sqlite -} // namespace Plugin -} // namespace WPEFramework diff --git a/PersistentStore/sqlite/StoreInspector.cpp b/PersistentStore/sqlite/StoreInspector.cpp new file mode 100644 index 0000000000..690a1478f2 --- /dev/null +++ b/PersistentStore/sqlite/StoreInspector.cpp @@ -0,0 +1,10 @@ +#include "StoreInspector.h" + +namespace WPEFramework { +namespace Plugin { + namespace Sqlite { + using SqliteStoreInspector = StoreInspector; + SERVICE_REGISTRATION(SqliteStoreInspector, 1, 0); + } // namespace Sqlite +} // namespace Plugin +} // namespace WPEFramework diff --git a/PersistentStore/sqlite/StoreInspector.h b/PersistentStore/sqlite/StoreInspector.h new file mode 100644 index 0000000000..7ca7c20be4 --- /dev/null +++ b/PersistentStore/sqlite/StoreInspector.h @@ -0,0 +1,190 @@ +/* + * If not stated otherwise in this file or this component's LICENSE file the + * following copyright and licenses apply: + * + * Copyright 2022 RDK Management + * + * 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 "../Module.h" +#include +#include + +namespace WPEFramework { +namespace Plugin { + namespace Sqlite { + + class StoreInspector : public Exchange::IStoreInspector { + private: + StoreInspector(const StoreInspector&) = delete; + StoreInspector& operator=(const StoreInspector&) = delete; + + typedef RPC::IteratorType NamespaceSizeIterator; + + public: + StoreInspector() + : StoreInspector(getenv(PATH_ENV)) + { + } + StoreInspector(const string& path) + : IStoreInspector() + , _path(path) + { + Open(); + } + ~StoreInspector() override + { + Close(); + } + + private: + void Open() + { + Core::File file(_path); + Core::Directory(file.PathName().c_str()).CreatePath(); + auto rc = sqlite3_open(_path.c_str(), &_data); + if (rc != SQLITE_OK) { + OnError(__FUNCTION__, rc); + } + const std::vector statements = { + "pragma foreign_keys = on;", + "pragma busy_timeout = 1000000;", + "create table if not exists namespace (id integer primary key,name text unique);", + "create table if not exists item (ns integer,key text,value text,foreign key(ns) references namespace(id) on delete cascade on update no action,unique(ns,key) on conflict replace);" + }; + for (auto& sql : statements) { + auto rc = sqlite3_exec(_data, sql.c_str(), nullptr, nullptr, nullptr); + if (rc != SQLITE_OK) { + OnError(__FUNCTION__, rc); + } + } + } + void Close() + { + auto rc = sqlite3_close_v2(_data); + if (rc != SQLITE_OK) { + OnError(__FUNCTION__, rc); + } + } + + public: + uint32_t GetKeys(const ScopeType scope, const string& ns, RPC::IStringIterator*& keys) override + { + ASSERT(scope == ScopeType::DEVICE); + + uint32_t result; + + sqlite3_stmt* stmt; + sqlite3_prepare_v2(_data, "select key" + " from item" + " where ns in (select id from namespace where name = ?)" + ";", + -1, &stmt, nullptr); + sqlite3_bind_text(stmt, 1, ns.c_str(), -1, SQLITE_TRANSIENT); + std::list list; + int rc; + while ((rc = sqlite3_step(stmt)) == SQLITE_ROW) { + list.emplace_back((const char*)sqlite3_column_text(stmt, 0)); + } + sqlite3_finalize(stmt); + + if (rc == SQLITE_DONE) { + keys = (Core::Service::Create(list)); + result = Core::ERROR_NONE; + } else { + OnError(__FUNCTION__, rc); + result = Core::ERROR_GENERAL; + } + + return result; + } + uint32_t GetNamespaces(const ScopeType scope, RPC::IStringIterator*& namespaces) override + { + ASSERT(scope == ScopeType::DEVICE); + + uint32_t result; + + sqlite3_stmt* stmt; + sqlite3_prepare_v2(_data, "select name from namespace;", -1, &stmt, nullptr); + std::list list; + int rc; + while ((rc = sqlite3_step(stmt)) == SQLITE_ROW) { + list.emplace_back((const char*)sqlite3_column_text(stmt, 0)); + } + sqlite3_finalize(stmt); + + if (rc == SQLITE_DONE) { + namespaces = (Core::Service::Create(list)); + result = Core::ERROR_NONE; + } else { + OnError(__FUNCTION__, rc); + result = Core::ERROR_GENERAL; + } + + return result; + } + uint32_t GetStorageSizes(const ScopeType scope, INamespaceSizeIterator*& storageList) override + { + ASSERT(scope == ScopeType::DEVICE); + + uint32_t result; + + sqlite3_stmt* stmt; + sqlite3_prepare_v2(_data, "select name, sum(length(key)+length(value))" + " from item" + " inner join namespace on namespace.id = item.ns" + " group by name" + ";", + -1, &stmt, nullptr); + std::list list; + int rc; + while ((rc = sqlite3_step(stmt)) == SQLITE_ROW) { + NamespaceSize namespaceSize; + namespaceSize.ns = (const char*)sqlite3_column_text(stmt, 0); + namespaceSize.size = sqlite3_column_int(stmt, 1); + list.emplace_back(namespaceSize); + } + sqlite3_finalize(stmt); + + if (rc == SQLITE_DONE) { + storageList = (Core::Service::Create(list)); + result = Core::ERROR_NONE; + } else { + OnError(__FUNCTION__, rc); + result = Core::ERROR_GENERAL; + } + + return result; + } + + BEGIN_INTERFACE_MAP(StoreInspector) + INTERFACE_ENTRY(IStoreInspector) + END_INTERFACE_MAP + + private: + void OnError(const char* fn, const int status) const + { + TRACE(Trace::Error, (_T("%s sqlite error %d"), status)); + } + + private: + const string _path; + sqlite3* _data; + }; + + } // namespace Sqlite +} // namespace Plugin +} // namespace WPEFramework diff --git a/PersistentStore/sqlite/StoreInspectorType.h b/PersistentStore/sqlite/StoreInspectorType.h deleted file mode 100644 index f8c10cf21d..0000000000 --- a/PersistentStore/sqlite/StoreInspectorType.h +++ /dev/null @@ -1,161 +0,0 @@ -/* - * If not stated otherwise in this file or this component's LICENSE file the - * following copyright and licenses apply: - * - * Copyright 2022 RDK Management - * - * 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 - -namespace WPEFramework { -namespace Plugin { - namespace Sqlite { - - template - class StoreInspectorType : public Exchange::IStoreInspector, protected ACTUALHANDLE { - private: - StoreInspectorType(const StoreInspectorType&) = delete; - StoreInspectorType& operator=(const StoreInspectorType&) = delete; - - typedef RPC::IteratorType NamespaceSizeIterator; - - public: - template - StoreInspectorType(Args&&... args) - : Exchange::IStoreInspector() - , ACTUALHANDLE(std::forward(args)...) - { - } - ~StoreInspectorType() override = default; - - public: - // IStoreInspector methods - - uint32_t GetKeys(const ScopeType scope, const string& ns, RPC::IStringIterator*& keys) override - { - ASSERT(scope == ScopeType::DEVICE); - - uint32_t result; - - sqlite3_stmt* stmt; - sqlite3_prepare_v2(*this, "SELECT key" - " FROM item" - " where ns in (select id from namespace where name = ?)" - ";", - -1, &stmt, NULL); - - sqlite3_bind_text(stmt, 1, ns.c_str(), -1, SQLITE_TRANSIENT); - - std::list list; - do { - auto rc = sqlite3_step(stmt); - if (rc == SQLITE_ROW) { - list.emplace_back((const char*)sqlite3_column_text(stmt, 0)); - } else { - if (rc == SQLITE_DONE) { - keys = (Core::Service::Create(list)); - result = Core::ERROR_NONE; - } else { - TRACE(Trace::Error, (_T("Sqlite select keys error %d"), rc)); - result = Core::ERROR_GENERAL; - } - break; - } - } while (true); - - sqlite3_finalize(stmt); - - return result; - } - uint32_t GetNamespaces(const ScopeType scope, RPC::IStringIterator*& namespaces) override - { - ASSERT(scope == ScopeType::DEVICE); - - uint32_t result; - - sqlite3_stmt* stmt; - sqlite3_prepare_v2(*this, "SELECT name FROM namespace;", -1, &stmt, NULL); - - std::list list; - do { - auto rc = sqlite3_step(stmt); - if (rc == SQLITE_ROW) { - list.emplace_back((const char*)sqlite3_column_text(stmt, 0)); - } else { - if (rc == SQLITE_DONE) { - namespaces = (Core::Service::Create(list)); - result = Core::ERROR_NONE; - } else { - TRACE(Trace::Error, (_T("Sqlite select namespaces error %d"), rc)); - result = Core::ERROR_GENERAL; - } - break; - } - } while (true); - - sqlite3_finalize(stmt); - - return result; - } - uint32_t GetStorageSizes(const ScopeType scope, INamespaceSizeIterator*& storageList) override - { - ASSERT(scope == ScopeType::DEVICE); - - uint32_t result; - - sqlite3_stmt* stmt; - sqlite3_prepare_v2(*this, "SELECT name, sum(length(key)+length(value))" - " FROM item" - " INNER JOIN namespace ON namespace.id = item.ns" - " GROUP BY name" - ";", - -1, &stmt, NULL); - - std::list list; - do { - auto rc = sqlite3_step(stmt); - if (rc == SQLITE_ROW) { - NamespaceSize namespaceSize; - namespaceSize.ns = (const char*)sqlite3_column_text(stmt, 0); - namespaceSize.size = sqlite3_column_int(stmt, 1); - list.emplace_back(namespaceSize); - } else { - if (rc == SQLITE_DONE) { - storageList = (Core::Service::Create(list)); - result = Core::ERROR_NONE; - } else { - TRACE(Trace::Error, (_T("Sqlite select sizes error %d"), rc)); - result = Core::ERROR_GENERAL; - } - break; - } - } while (true); - - sqlite3_finalize(stmt); - - return result; - } - - BEGIN_INTERFACE_MAP(StoreInspectorType) - INTERFACE_ENTRY(Exchange::IStoreInspector) - END_INTERFACE_MAP - }; - - } // namespace Sqlite -} // namespace Plugin -} // namespace WPEFramework diff --git a/PersistentStore/sqlite/StoreLimit.cpp b/PersistentStore/sqlite/StoreLimit.cpp new file mode 100644 index 0000000000..be638cc1aa --- /dev/null +++ b/PersistentStore/sqlite/StoreLimit.cpp @@ -0,0 +1,10 @@ +#include "StoreLimit.h" + +namespace WPEFramework { +namespace Plugin { + namespace Sqlite { + using SqliteStoreLimit = StoreLimit; + SERVICE_REGISTRATION(SqliteStoreLimit, 1, 0); + } // namespace Sqlite +} // namespace Plugin +} // namespace WPEFramework diff --git a/PersistentStore/sqlite/StoreLimit.h b/PersistentStore/sqlite/StoreLimit.h new file mode 100644 index 0000000000..f91936485c --- /dev/null +++ b/PersistentStore/sqlite/StoreLimit.h @@ -0,0 +1,183 @@ +/* + * If not stated otherwise in this file or this component's LICENSE file the + * following copyright and licenses apply: + * + * Copyright 2022 RDK Management + * + * 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 "../Module.h" +#include +#include + +namespace WPEFramework { +namespace Plugin { + namespace Sqlite { + + class StoreLimit : public Exchange::IStoreLimit { + private: + StoreLimit(const StoreLimit&) = delete; + StoreLimit& operator=(const StoreLimit&) = delete; + + public: + StoreLimit() + : StoreLimit( + getenv(PATH_ENV), + std::stoul(getenv(MAXSIZE_ENV)), + std::stoul(getenv(MAXVALUE_ENV))) + { + } + StoreLimit(const string& path, const uint64_t maxSize, const uint64_t maxValue) + : IStoreLimit() + , _path(path) + , _maxSize(maxSize) + , _maxValue(maxValue) + { + Open(); + } + ~StoreLimit() override + { + Close(); + } + + private: + void Open() + { + Core::File file(_path); + Core::Directory(file.PathName().c_str()).CreatePath(); + auto rc = sqlite3_open(_path.c_str(), &_data); + if (rc != SQLITE_OK) { + OnError(__FUNCTION__, rc); + } + const std::vector statements = { + "pragma foreign_keys = on;", + "pragma busy_timeout = 1000000;", + "create table if not exists namespace (id integer primary key,name text unique);", + "create table if not exists item (ns integer,key text,value text,foreign key(ns) references namespace(id) on delete cascade on update no action,unique(ns,key) on conflict replace);", + "create table if not exists limits (n integer,size integer,foreign key(n) references namespace(id) on delete cascade on update no action,unique(n) on conflict replace);", + "create temporary trigger if not exists ns_empty insert on namespace begin select case when length(new.name) = 0 then raise (fail, 'empty') end; end;", + "create temporary trigger if not exists ns_maxvalue insert on namespace begin select case when length(new.name) > " + std::to_string(_maxValue) + " then raise (fail, 'max value') end; end;", + "create temporary trigger if not exists ns_maxsize insert on namespace begin select case when (select sum(s) from (select sum(length(key)+length(value)) s from item union all select sum(length(name)) s from namespace union all select length(new.name) s)) > " + std::to_string(_maxSize) + " then raise (fail, 'max size') end; end;" + }; + for (auto& sql : statements) { + auto rc = sqlite3_exec(_data, sql.c_str(), nullptr, nullptr, nullptr); + if (rc != SQLITE_OK) { + OnError(__FUNCTION__, rc); + } + } + } + void Close() + { + auto rc = sqlite3_close_v2(_data); + if (rc != SQLITE_OK) { + OnError(__FUNCTION__, rc); + } + } + + public: + uint32_t SetNamespaceStorageLimit(const ScopeType scope, const string& ns, const uint32_t size) override + { + ASSERT(scope == ScopeType::DEVICE); + + uint32_t result; + + sqlite3_stmt* stmt; + sqlite3_prepare_v2(_data, "insert or ignore into namespace (name) values (?);", + -1, &stmt, nullptr); + sqlite3_bind_text(stmt, 1, ns.c_str(), -1, SQLITE_TRANSIENT); + auto rc = sqlite3_step(stmt); + sqlite3_finalize(stmt); + if (rc == SQLITE_DONE) { + sqlite3_prepare_v2(_data, "insert into limits (n,size)" + " select id, ?" + " from namespace" + " where name = ?" + ";", + -1, &stmt, nullptr); + sqlite3_bind_int(stmt, 1, size); + sqlite3_bind_text(stmt, 2, ns.c_str(), -1, SQLITE_TRANSIENT); + rc = sqlite3_step(stmt); + sqlite3_finalize(stmt); + } + + if (rc == SQLITE_DONE) { + result = Core::ERROR_NONE; + } else { + OnError(__FUNCTION__, rc); + if (rc == SQLITE_CONSTRAINT) { + result = Core::ERROR_INVALID_INPUT_LENGTH; + } else { + result = Core::ERROR_GENERAL; + } + } + + return result; + } + uint32_t GetNamespaceStorageLimit(const ScopeType scope, const string& ns, uint32_t& size) override + { + ASSERT(scope == ScopeType::DEVICE); + + uint32_t result; + + uint32_t s; + sqlite3_stmt* stmt; + sqlite3_prepare_v2(_data, "select size" + " from limits" + " inner join namespace on namespace.id = limits.n" + " where name = ?" + ";", + -1, &stmt, nullptr); + sqlite3_bind_text(stmt, 1, ns.c_str(), -1, SQLITE_TRANSIENT); + auto rc = sqlite3_step(stmt); + if (rc == SQLITE_ROW) { + s = (uint32_t)sqlite3_column_int(stmt, 0); + result = Core::ERROR_NONE; + } + sqlite3_finalize(stmt); + + if (rc == SQLITE_ROW) { + size = s; + result = Core::ERROR_NONE; + } else if (rc == SQLITE_DONE) { + result = Core::ERROR_NOT_EXIST; + } else { + OnError(__FUNCTION__, rc); + result = Core::ERROR_GENERAL; + } + + return result; + } + + BEGIN_INTERFACE_MAP(StoreLimit) + INTERFACE_ENTRY(IStoreLimit) + END_INTERFACE_MAP + + private: + void OnError(const char* fn, const int status) const + { + TRACE(Trace::Error, (_T("%s sqlite error %d"), status)); + } + + private: + const string _path; + const uint64_t _maxSize; + const uint64_t _maxValue; + sqlite3* _data; + }; + + } // namespace Sqlite +} // namespace Plugin +} // namespace WPEFramework diff --git a/PersistentStore/sqlite/StoreLimitType.h b/PersistentStore/sqlite/StoreLimitType.h deleted file mode 100644 index c9d615bc41..0000000000 --- a/PersistentStore/sqlite/StoreLimitType.h +++ /dev/null @@ -1,138 +0,0 @@ -/* - * If not stated otherwise in this file or this component's LICENSE file the - * following copyright and licenses apply: - * - * Copyright 2022 RDK Management - * - * 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 - -namespace WPEFramework { -namespace Plugin { - namespace Sqlite { - - template - class StoreLimitType : public Exchange::IStoreLimit, protected ACTUALHANDLE { - private: - StoreLimitType(const StoreLimitType&) = delete; - StoreLimitType& operator=(const StoreLimitType&) = delete; - - public: - template - StoreLimitType(Args&&... args) - : Exchange::IStoreLimit() - , ACTUALHANDLE(std::forward(args)...) - { - } - ~StoreLimitType() override = default; - - public: - // IStoreLimit methods - - uint32_t SetNamespaceStorageLimit(const ScopeType scope, const string& ns, const uint32_t size) override - { - ASSERT(scope == ScopeType::DEVICE); - - uint32_t result; - - sqlite3_stmt* stmt; - sqlite3_prepare_v2(*this, "INSERT OR IGNORE INTO namespace (name) values (?);", - -1, &stmt, nullptr); - - sqlite3_bind_text(stmt, 1, ns.c_str(), -1, SQLITE_TRANSIENT); - - auto rc = sqlite3_step(stmt); - sqlite3_finalize(stmt); - - if (rc == SQLITE_DONE) { - sqlite3_prepare_v2(*this, "INSERT INTO limits (n,size)" - " SELECT id, ?" - " FROM namespace" - " WHERE name = ?" - ";", - -1, &stmt, nullptr); - - sqlite3_bind_int(stmt, 1, size); - sqlite3_bind_text(stmt, 2, ns.c_str(), -1, SQLITE_TRANSIENT); - - rc = sqlite3_step(stmt); - sqlite3_finalize(stmt); - - if (rc == SQLITE_DONE) { - result = Core::ERROR_NONE; - } else { - TRACE(Trace::Error, (_T("Sqlite insert limit error %d"), rc)); - if (rc == SQLITE_READONLY || rc == SQLITE_CORRUPT) { - result = Core::ERROR_UNAVAILABLE; - } else { - result = Core::ERROR_GENERAL; - } - } - } else if (rc == SQLITE_CONSTRAINT) { - result = Core::ERROR_INVALID_INPUT_LENGTH; - } else { - TRACE(Trace::Error, (_T("Sqlite insert namespace error %d"), rc)); - if (rc == SQLITE_READONLY || rc == SQLITE_CORRUPT) { - result = Core::ERROR_UNAVAILABLE; - } else { - result = Core::ERROR_GENERAL; - } - } - - return result; - } - uint32_t GetNamespaceStorageLimit(const ScopeType scope, const string& ns, uint32_t& size) override - { - ASSERT(scope == ScopeType::DEVICE); - - uint32_t result; - - sqlite3_stmt* stmt; - sqlite3_prepare_v2(*this, "SELECT size" - " FROM limits" - " INNER JOIN namespace ON namespace.id = limits.n" - " where name = ?" - ";", - -1, &stmt, nullptr); - - sqlite3_bind_text(stmt, 1, ns.c_str(), -1, SQLITE_TRANSIENT); - - auto rc = sqlite3_step(stmt); - if (rc == SQLITE_ROW) { - size = (uint32_t)sqlite3_column_int(stmt, 0); - result = Core::ERROR_NONE; - } else if (rc == SQLITE_DONE) { - result = Core::ERROR_NOT_EXIST; - } else { - TRACE(Trace::Error, (_T("Sqlite select limit error %d"), rc)); - result = Core::ERROR_GENERAL; - } - - sqlite3_finalize(stmt); - - return result; - } - - BEGIN_INTERFACE_MAP(StoreLimitType) - INTERFACE_ENTRY(Exchange::IStoreLimit) - END_INTERFACE_MAP - }; - - } // namespace Sqlite -} // namespace Plugin -} // namespace WPEFramework diff --git a/PersistentStore/sqlite/l1test/CMakeLists.txt b/PersistentStore/sqlite/l1test/CMakeLists.txt index d79c5af548..269c252135 100644 --- a/PersistentStore/sqlite/l1test/CMakeLists.txt +++ b/PersistentStore/sqlite/l1test/CMakeLists.txt @@ -15,9 +15,9 @@ # See the License for the specific language governing permissions and # limitations under the License. -project(sqlitel1test) +cmake_minimum_required(VERSION 3.14) -cmake_minimum_required(VERSION 3.11) +project(sqlitel1test) set(CMAKE_CXX_STANDARD 11) @@ -28,35 +28,24 @@ FetchContent_Declare( ) FetchContent_MakeAvailable(googletest) -find_package(WPEFramework) +find_package(WPEFramework REQUIRED) find_package(${NAMESPACE}Plugins REQUIRED) -find_package(PkgConfig) -pkg_search_module(SQLITE REQUIRED sqlite3) - add_executable(${PROJECT_NAME} ../../Module.cpp - ../Handle.cpp - HandleTest.cpp Store2Test.cpp StoreCacheTest.cpp StoreInspectorTest.cpp - Store2WithReconnectTest.cpp - ) - -add_definitions(${SQLITE_CFLAGS_OTHER}) - -link_directories(${SQLITE_LIBRARY_DIRS}) + StoreLimitTest.cpp +) target_link_libraries(${PROJECT_NAME} gmock_main ${NAMESPACE}Plugins::${NAMESPACE}Plugins - ${SQLITE_LIBRARIES} - ) +) -target_include_directories(${PROJECT_NAME} - PUBLIC - ../../ - ) +find_package(PkgConfig REQUIRED) +pkg_search_module(SQLITE REQUIRED sqlite3) +target_link_libraries(${PROJECT_NAME} ${SQLITE_LIBRARIES}) install(TARGETS ${PROJECT_NAME} DESTINATION bin) diff --git a/PersistentStore/sqlite/l1test/HandleTest.cpp b/PersistentStore/sqlite/l1test/HandleTest.cpp deleted file mode 100644 index 3327265050..0000000000 --- a/PersistentStore/sqlite/l1test/HandleTest.cpp +++ /dev/null @@ -1,48 +0,0 @@ -#include -#include - -#include "../Handle.h" - -using namespace WPEFramework; -using namespace WPEFramework::Plugin; - -using ::testing::Eq; -using ::testing::IsTrue; -using ::testing::Test; - -const std::string Path = "/tmp/sqlite/l1test/handletest"; -const uint32_t MaxSize = 100; -const uint32_t MaxValue = 10; -const uint32_t Limit = 50; - -class AHandle : public Test { -protected: - Core::ProxyType handle; - ~AHandle() override = default; - void SetUp() override - { - Core::File(Path).Destroy(); - // File is destroyed - - Core::SystemInfo::SetEnvironment(_T("PERSISTENTSTORE_PATH"), Path); - Core::SystemInfo::SetEnvironment(_T("PERSISTENTSTORE_MAXSIZE"), std::to_string(MaxSize)); - Core::SystemInfo::SetEnvironment(_T("PERSISTENTSTORE_MAXVALUE"), std::to_string(MaxValue)); - Core::SystemInfo::SetEnvironment(_T("PERSISTENTSTORE_LIMIT"), std::to_string(Limit)); - handle = Core::ProxyType::Create(); - } -}; - -TEST_F(AHandle, GetsFilename) -{ - EXPECT_THAT(sqlite3_db_filename((sqlite3*)(*handle), nullptr), Eq(Path)); -} - -TEST_F(AHandle, CreatesFile) -{ - EXPECT_THAT(Core::File(Path).Exists(), IsTrue()); -} - -TEST_F(AHandle, Reopens) -{ - EXPECT_THAT(handle->Open(), Eq(Core::ERROR_NONE)); -} diff --git a/PersistentStore/sqlite/l1test/Store2NotificationMock.h b/PersistentStore/sqlite/l1test/Store2NotificationMock.h new file mode 100644 index 0000000000..4e422bf710 --- /dev/null +++ b/PersistentStore/sqlite/l1test/Store2NotificationMock.h @@ -0,0 +1,13 @@ +#pragma once + +#include +#include + +class Store2NotificationMock : public WPEFramework::Exchange::IStore2::INotification { +public: + ~Store2NotificationMock() override = default; + MOCK_METHOD(void, ValueChanged, (const WPEFramework::Exchange::IStore2::ScopeType scope, const string& ns, const string& key, const string& value), (override)); + BEGIN_INTERFACE_MAP(Store2NotificationMock) + INTERFACE_ENTRY(INotification) + END_INTERFACE_MAP +}; diff --git a/PersistentStore/sqlite/l1test/Store2Test.cpp b/PersistentStore/sqlite/l1test/Store2Test.cpp index 9590fb9881..baa4a5a62f 100644 --- a/PersistentStore/sqlite/l1test/Store2Test.cpp +++ b/PersistentStore/sqlite/l1test/Store2Test.cpp @@ -1,286 +1,191 @@ #include #include -#include "../Handle.h" -#include "../Store2Type.h" - -using namespace WPEFramework; -using namespace WPEFramework::Plugin; +#include "../Store2.h" +#include "Store2NotificationMock.h" +using ::testing::_; using ::testing::Eq; +using ::testing::Gt; +using ::testing::Invoke; using ::testing::Le; +using ::testing::NiceMock; using ::testing::Test; - -const std::string Path = "/tmp/sqlite/l1test/store2test"; -const uint32_t MaxSize = 100; -const uint32_t MaxValue = 10; -const uint32_t Limit = 50; +using ::WPEFramework::Exchange::IStore2; +using ::WPEFramework::Plugin::Sqlite::Store2; + +const auto kPath = "/tmp/persistentstore/sqlite/l1test/store2test"; +const auto kMaxSize = 100; +const auto kMaxValue = 10; +const auto kLimit = 50; +const auto kValue = "value_1"; +const auto kKey = "key_1"; +const auto kAppId = "app_id_1"; +const auto kTtl = 2; +const auto kNoTtl = 0; +const auto kScope = IStore2::ScopeType::DEVICE; +const auto kEmpty = ""; +const auto kOversize = "this is too large"; +const auto kUnknown = "unknown"; class AStore2 : public Test { protected: - Exchange::IStore2* store2; - ~AStore2() override = default; - void SetUp() override + IStore2* store2; + AStore2() + : store2(WPEFramework::Core::Service::Create(kPath, kMaxSize, kMaxValue, kLimit)) { - Core::File(Path).Destroy(); - // File is destroyed - - Core::SystemInfo::SetEnvironment(_T("PERSISTENTSTORE_PATH"), Path); - Core::SystemInfo::SetEnvironment(_T("PERSISTENTSTORE_MAXSIZE"), std::to_string(MaxSize)); - Core::SystemInfo::SetEnvironment(_T("PERSISTENTSTORE_MAXVALUE"), std::to_string(MaxValue)); - Core::SystemInfo::SetEnvironment(_T("PERSISTENTSTORE_LIMIT"), std::to_string(Limit)); - store2 = Core::Service>::Create(); } - void TearDown() override + ~AStore2() override { store2->Release(); } }; -TEST_F(AStore2, DoesNotSetEmptyNamespace) +TEST_F(AStore2, DoesNotSetValueWhenNamespaceEmpty) { - EXPECT_THAT(store2->SetValue(Exchange::IStore2::ScopeType::DEVICE, "", "x", "x", 0), Eq(Core::ERROR_INVALID_INPUT_LENGTH)); + EXPECT_THAT(store2->SetValue(kScope, kEmpty, kKey, kValue, kNoTtl), Eq(WPEFramework::Core::ERROR_INVALID_INPUT_LENGTH)); } -TEST_F(AStore2, DoesNotSetEmptyKey) +TEST_F(AStore2, DoesNotSetValueWhenKeyEmpty) { - EXPECT_THAT(store2->SetValue(Exchange::IStore2::ScopeType::DEVICE, "x", "", "x", 0), Eq(Core::ERROR_INVALID_INPUT_LENGTH)); + EXPECT_THAT(store2->SetValue(kScope, kAppId, kEmpty, kValue, kNoTtl), Eq(WPEFramework::Core::ERROR_INVALID_INPUT_LENGTH)); } -TEST_F(AStore2, DoesNotSetTooLargeNamespaceName) +TEST_F(AStore2, DoesNotSetValueWhenNamespaceOversize) { - EXPECT_THAT(store2->SetValue(Exchange::IStore2::ScopeType::DEVICE, "this is too large", "x", "x", 0), Eq(Core::ERROR_INVALID_INPUT_LENGTH)); + EXPECT_THAT(store2->SetValue(kScope, kOversize, kKey, kValue, kNoTtl), Eq(WPEFramework::Core::ERROR_INVALID_INPUT_LENGTH)); } -TEST_F(AStore2, DoesNotSetTooLargeKey) +TEST_F(AStore2, DoesNotSetValueWhenKeyOversize) { - EXPECT_THAT(store2->SetValue(Exchange::IStore2::ScopeType::DEVICE, "x", "this is too large", "x", 0), Eq(Core::ERROR_INVALID_INPUT_LENGTH)); + EXPECT_THAT(store2->SetValue(kScope, kAppId, kOversize, kValue, kNoTtl), Eq(WPEFramework::Core::ERROR_INVALID_INPUT_LENGTH)); } -TEST_F(AStore2, DoesNotSetTooLargeValue) +TEST_F(AStore2, DoesNotSetValueWhenValueOversize) { - EXPECT_THAT(store2->SetValue(Exchange::IStore2::ScopeType::DEVICE, "x", "x", "this is too large", 0), Eq(Core::ERROR_INVALID_INPUT_LENGTH)); + EXPECT_THAT(store2->SetValue(kScope, kAppId, kKey, kOversize, kNoTtl), Eq(WPEFramework::Core::ERROR_INVALID_INPUT_LENGTH)); } -TEST_F(AStore2, DoesNotGetValueInUnknownNamespace) +TEST_F(AStore2, DoesNotGetValueWhenNamespaceUnknown) { string value; uint32_t ttl; - EXPECT_THAT(store2->GetValue(Exchange::IStore2::ScopeType::DEVICE, "x", "x", value, ttl), Eq(Core::ERROR_NOT_EXIST)); + EXPECT_THAT(store2->GetValue(kScope, kUnknown, kKey, value, ttl), Eq(WPEFramework::Core::ERROR_NOT_EXIST)); } -TEST_F(AStore2, DeletesKeyInUnknownNamespaceWithoutError) +TEST_F(AStore2, DeletesKeyWhenNamespaceUnknown) { - EXPECT_THAT(store2->DeleteKey(Exchange::IStore2::ScopeType::DEVICE, "x", "x"), Eq(Core::ERROR_NONE)); + EXPECT_THAT(store2->DeleteKey(kScope, kUnknown, kKey), Eq(WPEFramework::Core::ERROR_NONE)); } -TEST_F(AStore2, DeletesUnknownNamespaceWithoutError) +TEST_F(AStore2, DeletesNamespaceWhenNamespaceUnknown) { - EXPECT_THAT(store2->DeleteNamespace(Exchange::IStore2::ScopeType::DEVICE, "x"), Eq(Core::ERROR_NONE)); + EXPECT_THAT(store2->DeleteNamespace(kScope, kUnknown), Eq(WPEFramework::Core::ERROR_NONE)); } -TEST_F(AStore2, SetsEmptyValueWithoutError) +TEST_F(AStore2, SetsValueWhenValueEmpty) { - EXPECT_THAT(store2->SetValue(Exchange::IStore2::ScopeType::DEVICE, "ns1", "key1", "", 0), Eq(Core::ERROR_NONE)); + ASSERT_THAT(store2->SetValue(kScope, kAppId, kKey, kEmpty, kNoTtl), Eq(WPEFramework::Core::ERROR_NONE)); string value; uint32_t ttl; - EXPECT_THAT(store2->GetValue(Exchange::IStore2::ScopeType::DEVICE, "ns1", "key1", value, ttl), Eq(Core::ERROR_NONE)); - EXPECT_THAT(value, Eq("")); - EXPECT_THAT(ttl, Eq(0)); + ASSERT_THAT(store2->GetValue(kScope, kAppId, kKey, value, ttl), Eq(WPEFramework::Core::ERROR_NONE)); + EXPECT_THAT(value, Eq(kEmpty)); + EXPECT_THAT(ttl, Eq(kNoTtl)); } -TEST_F(AStore2, GetsValueWithTtlThatWasSet) +TEST_F(AStore2, DoesNotSetValueWhenClockNotSync) { - EXPECT_THAT(store2->SetValue(Exchange::IStore2::ScopeType::DEVICE, "ns1", "key1", "value1", 100), Eq(Core::ERROR_NONE)); - // Value with ttl set + EXPECT_THAT(store2->SetValue(kScope, kAppId, kKey, kValue, kTtl), Eq(WPEFramework::Core::ERROR_PENDING_CONDITIONS)); +} +TEST_F(AStore2, GetsValueWhenTtlNotExpired) +{ + WPEFramework::Core::SystemInfo::SetEnvironment(CLOCKSYNC_ENV, kEmpty); + ASSERT_THAT(store2->SetValue(kScope, kAppId, kKey, kValue, kTtl), Eq(WPEFramework::Core::ERROR_NONE)); string value; uint32_t ttl; - EXPECT_THAT(store2->GetValue(Exchange::IStore2::ScopeType::DEVICE, "ns1", "key1", value, ttl), Eq(Core::ERROR_NONE)); - EXPECT_THAT(value, Eq("value1")); - EXPECT_THAT(ttl, Le(100)); + ASSERT_THAT(store2->GetValue(kScope, kAppId, kKey, value, ttl), Eq(WPEFramework::Core::ERROR_NONE)); + EXPECT_THAT(value, Eq(kValue)); + EXPECT_THAT(ttl, Le(kTtl)); + EXPECT_THAT(ttl, Gt(kNoTtl)); + ::unsetenv(CLOCKSYNC_ENV); } -TEST_F(AStore2, DoesNotGetExpiredValueThatWasSet) +TEST_F(AStore2, DoesNotGetValueWhenTtlExpired) { - EXPECT_THAT(store2->SetValue(Exchange::IStore2::ScopeType::DEVICE, "ns1", "key1", "value1", 1), Eq(Core::ERROR_NONE)); - // Value with ttl set - Core::Event lock(false, true); - lock.Lock(1000); - // Value expired - + WPEFramework::Core::SystemInfo::SetEnvironment(CLOCKSYNC_ENV, kEmpty); + ASSERT_THAT(store2->SetValue(kScope, kAppId, kKey, kValue, kTtl), Eq(WPEFramework::Core::ERROR_NONE)); + WPEFramework::Core::Event lock(false, true); + lock.Lock(kTtl * WPEFramework::Core::Time::MilliSecondsPerSecond); string value; uint32_t ttl; - EXPECT_THAT(store2->GetValue(Exchange::IStore2::ScopeType::DEVICE, "ns1", "key1", value, ttl), Eq(Core::ERROR_UNKNOWN_KEY)); + EXPECT_THAT(store2->GetValue(kScope, kAppId, kKey, value, ttl), Eq(WPEFramework::Core::ERROR_UNKNOWN_KEY)); + ::unsetenv(CLOCKSYNC_ENV); } -class AStore2WithNotification : public AStore2 { -protected: - class Store2Notification : public Exchange::IStore2::INotification { +TEST_F(AStore2, ValueChangedWhenSetValue) +{ + class Store2Notification : public NiceMock { public: - Core::Event _valueChanged; - Exchange::IStore2::ScopeType _scope; - string _ns; - string _key; - string _value; Store2Notification() - : _valueChanged(false, true) { + EXPECT_CALL(*this, ValueChanged(_, _, _, _)) + .WillRepeatedly(Invoke( + [](const IStore2::ScopeType scope, const string& ns, const string& key, const string& value) { + EXPECT_THAT(scope, Eq(kScope)); + EXPECT_THAT(ns, Eq(kAppId)); + EXPECT_THAT(key, Eq(kKey)); + EXPECT_THAT(value, Eq(kValue)); + return WPEFramework::Core::ERROR_NONE; + })); } - void ValueChanged(const Exchange::IStore2::ScopeType scope, const string& ns, const string& key, const string& value) override - { - _valueChanged.SetEvent(); - _scope = scope; - _ns = ns; - _key = key; - _value = value; - } - - BEGIN_INTERFACE_MAP(Store2Notification) - INTERFACE_ENTRY(Exchange::IStore2::INotification) - END_INTERFACE_MAP }; - Core::Sink sink; - ~AStore2WithNotification() override = default; - void SetUp() override - { - AStore2::SetUp(); - store2->Register(&sink); - } - void TearDown() override - { - store2->Unregister(&sink); - AStore2::TearDown(); - } -}; - -TEST_F(AStore2WithNotification, TriggersNotificationWhenValueIsSet) -{ - ASSERT_THAT(store2->SetValue(Exchange::IStore2::ScopeType::DEVICE, "ns1", "key1", "value1", 0), Eq(Core::ERROR_NONE)); - // Value is set - - EXPECT_THAT(sink._valueChanged.Lock(100), Eq(Core::ERROR_NONE)); - EXPECT_THAT(sink._scope, Eq(Exchange::IStore2::ScopeType::DEVICE)); - EXPECT_THAT(sink._ns, Eq("ns1")); - EXPECT_THAT(sink._key, Eq("key1")); - EXPECT_THAT(sink._value, Eq("value1")); -} - -class AStore2WithValue : public AStore2 { -protected: - ~AStore2WithValue() override = default; - void SetUp() override - { - AStore2::SetUp(); - ASSERT_THAT(store2->SetValue(Exchange::IStore2::ScopeType::DEVICE, "ns1", "key1", "value1", 0), Eq(Core::ERROR_NONE)); - } -}; - -TEST_F(AStore2WithValue, DoesNotGetUnknownValueInExistingNamespace) -{ - string value; - uint32_t ttl; - EXPECT_THAT(store2->GetValue(Exchange::IStore2::ScopeType::DEVICE, "ns1", "x", value, ttl), Eq(Core::ERROR_UNKNOWN_KEY)); -} - -TEST_F(AStore2WithValue, DeletesUnknownKeyInExistingNamespaceWithoutError) -{ - EXPECT_THAT(store2->DeleteKey(Exchange::IStore2::ScopeType::DEVICE, "ns1", "x"), Eq(Core::ERROR_NONE)); + WPEFramework::Core::Sink sink; + store2->Register(&sink); + EXPECT_THAT(store2->SetValue(kScope, kAppId, kKey, kValue, kNoTtl), Eq(WPEFramework::Core::ERROR_NONE)); + store2->Unregister(&sink); } -TEST_F(AStore2WithValue, GetsValue) +TEST_F(AStore2, DoesNotGetValueWhenKeyUnknown) { + ASSERT_THAT(store2->SetValue(kScope, kAppId, kKey, kValue, kNoTtl), Eq(WPEFramework::Core::ERROR_NONE)); string value; uint32_t ttl; - EXPECT_THAT(store2->GetValue(Exchange::IStore2::ScopeType::DEVICE, "ns1", "key1", value, ttl), Eq(Core::ERROR_NONE)); - EXPECT_THAT(value, Eq("value1")); - EXPECT_THAT(ttl, Eq(0)); + EXPECT_THAT(store2->GetValue(kScope, kAppId, kUnknown, value, ttl), Eq(WPEFramework::Core::ERROR_UNKNOWN_KEY)); } -TEST_F(AStore2WithValue, UpdatesValue) +TEST_F(AStore2, DeletesKeyWhenKeyUnknown) { - EXPECT_THAT(store2->SetValue(Exchange::IStore2::ScopeType::DEVICE, "ns1", "key1", "value2", 0), Eq(Core::ERROR_NONE)); - string value; - uint32_t ttl; - EXPECT_THAT(store2->GetValue(Exchange::IStore2::ScopeType::DEVICE, "ns1", "key1", value, ttl), Eq(Core::ERROR_NONE)); - EXPECT_THAT(value, Eq("value2")); - EXPECT_THAT(ttl, Eq(0)); + ASSERT_THAT(store2->SetValue(kScope, kAppId, kKey, kValue, kNoTtl), Eq(WPEFramework::Core::ERROR_NONE)); + EXPECT_THAT(store2->DeleteKey(kScope, kAppId, kUnknown), Eq(WPEFramework::Core::ERROR_NONE)); } -TEST_F(AStore2WithValue, DoesNotGetDeletedValue) +TEST_F(AStore2, DeletesKey) { - EXPECT_THAT(store2->DeleteKey(Exchange::IStore2::ScopeType::DEVICE, "ns1", "key1"), Eq(Core::ERROR_NONE)); - // Value is deleted - + ASSERT_THAT(store2->SetValue(kScope, kAppId, kKey, kValue, kNoTtl), Eq(WPEFramework::Core::ERROR_NONE)); + ASSERT_THAT(store2->DeleteKey(kScope, kAppId, kKey), Eq(WPEFramework::Core::ERROR_NONE)); string value; uint32_t ttl; - EXPECT_THAT(store2->GetValue(Exchange::IStore2::ScopeType::DEVICE, "ns1", "key1", value, ttl), Eq(Core::ERROR_UNKNOWN_KEY)); + EXPECT_THAT(store2->GetValue(kScope, kAppId, kKey, value, ttl), Eq(WPEFramework::Core::ERROR_UNKNOWN_KEY)); } -TEST_F(AStore2WithValue, DoesNotGetValueInDeletedNamespace) +TEST_F(AStore2, DeletesNamespace) { - EXPECT_THAT(store2->DeleteNamespace(Exchange::IStore2::ScopeType::DEVICE, "ns1"), Eq(Core::ERROR_NONE)); - // Namespace is deleted - + ASSERT_THAT(store2->SetValue(kScope, kAppId, kKey, kValue, kNoTtl), Eq(WPEFramework::Core::ERROR_NONE)); + ASSERT_THAT(store2->DeleteNamespace(kScope, kAppId), Eq(WPEFramework::Core::ERROR_NONE)); string value; uint32_t ttl; - EXPECT_THAT(store2->GetValue(Exchange::IStore2::ScopeType::DEVICE, "ns1", "key1", value, ttl), Eq(Core::ERROR_NOT_EXIST)); -} - -class AStore2AtMaxSize : public AStore2 { -protected: - ~AStore2AtMaxSize() override = default; - void SetUp() override - { - AStore2::SetUp(); - for (int i = 0; i < 7; i++) { - ASSERT_THAT(store2->SetValue(Exchange::IStore2::ScopeType::DEVICE, "ns" + std::to_string(i), "key1", "value1", 0), Eq(Core::ERROR_NONE)); - } - ASSERT_THAT(store2->SetValue(Exchange::IStore2::ScopeType::DEVICE, "ns7", "key1", "va", 0), Eq(Core::ERROR_NONE)); - // Size is 100 - } -}; - -TEST_F(AStore2AtMaxSize, DoesNotSetValue) -{ - EXPECT_THAT(store2->SetValue(Exchange::IStore2::ScopeType::DEVICE, "x", "x", "x", 0), Eq(Core::ERROR_INVALID_INPUT_LENGTH)); + EXPECT_THAT(store2->GetValue(kScope, kAppId, kKey, value, ttl), Eq(WPEFramework::Core::ERROR_NOT_EXIST)); } -TEST_F(AStore2AtMaxSize, SetsValueAfterDeletesValue) +TEST_F(AStore2, DoesNotSetValueWhenReachedMaxSize) { - EXPECT_THAT(store2->DeleteKey(Exchange::IStore2::ScopeType::DEVICE, "ns1", "key1"), Eq(Core::ERROR_NONE)); - // Value deleted, size is 90 - - EXPECT_THAT(store2->SetValue(Exchange::IStore2::ScopeType::DEVICE, "ns1", "key2", "value2", 0), Eq(Core::ERROR_NONE)); -} - -class AStore2AtLimit : public AStore2 { -protected: - ~AStore2AtLimit() override = default; - void SetUp() override - { - AStore2::SetUp(); - for (int i = 0; i < 5; i++) { - ASSERT_THAT(store2->SetValue(Exchange::IStore2::ScopeType::DEVICE, "ns1", "key" + std::to_string(i), "value" + std::to_string(i), 0), Eq(Core::ERROR_NONE)); - } - // Namespace size is 50 - } -}; - -TEST_F(AStore2AtLimit, DoesNotSetValue) -{ - EXPECT_THAT(store2->SetValue(Exchange::IStore2::ScopeType::DEVICE, "ns1", "key5", "value5", 0), Eq(Core::ERROR_INVALID_INPUT_LENGTH)); -} - -TEST_F(AStore2AtLimit, SetsValueInAnotherNamespaceWithoutError) -{ - EXPECT_THAT(store2->SetValue(Exchange::IStore2::ScopeType::DEVICE, "ns2", "key1", "value1", 0), Eq(Core::ERROR_NONE)); -} - -TEST_F(AStore2AtLimit, SetsValueAfterDeletesValue) -{ - EXPECT_THAT(store2->DeleteKey(Exchange::IStore2::ScopeType::DEVICE, "ns1", "key1"), Eq(Core::ERROR_NONE)); - // Value deleted, namespace size is 40 - - EXPECT_THAT(store2->SetValue(Exchange::IStore2::ScopeType::DEVICE, "ns1", "key5", "value5", 0), Eq(Core::ERROR_NONE)); + ASSERT_THAT(store2->DeleteNamespace(kScope, kAppId), Eq(WPEFramework::Core::ERROR_NONE)); + ASSERT_THAT(store2->SetValue(kScope, "8InMXXU4hM", "YWKN74ODMf", "N0ed2C2h4n", kNoTtl), Eq(WPEFramework::Core::ERROR_NONE)); + ASSERT_THAT(store2->SetValue(kScope, "XhrICnuerw", "jPKODBDk5K", "d3BarkA5xF", kNoTtl), Eq(WPEFramework::Core::ERROR_NONE)); + ASSERT_THAT(store2->SetValue(kScope, "WNeBknDDI2", "GC96ZN6Fuq", "IBF2E1MLQh", kNoTtl), Eq(WPEFramework::Core::ERROR_NONE)); + EXPECT_THAT(store2->SetValue(kScope, kAppId, kKey, kValue, kNoTtl), Eq(WPEFramework::Core::ERROR_INVALID_INPUT_LENGTH)); + EXPECT_THAT(store2->DeleteNamespace(kScope, "8InMXXU4hM"), Eq(WPEFramework::Core::ERROR_NONE)); + EXPECT_THAT(store2->DeleteNamespace(kScope, "XhrICnuerw"), Eq(WPEFramework::Core::ERROR_NONE)); + EXPECT_THAT(store2->DeleteNamespace(kScope, "WNeBknDDI2"), Eq(WPEFramework::Core::ERROR_NONE)); } diff --git a/PersistentStore/sqlite/l1test/Store2WithReconnectTest.cpp b/PersistentStore/sqlite/l1test/Store2WithReconnectTest.cpp deleted file mode 100644 index f0df3f6d40..0000000000 --- a/PersistentStore/sqlite/l1test/Store2WithReconnectTest.cpp +++ /dev/null @@ -1,51 +0,0 @@ -#include -#include - -#include "../Handle.h" -#include "../Store2Type.h" -#include "../Store2WithReconnectType.h" - -using namespace WPEFramework; -using namespace WPEFramework::Plugin; - -using ::testing::Eq; -using ::testing::Test; - -const std::string Path = "/tmp/sqlite/l1test/store2withreconnecttest"; -const uint32_t MaxSize = 100; -const uint32_t MaxValue = 10; -const uint32_t Limit = 50; - -class AStore2WithReconnect : public Test { -protected: - Exchange::IStore2* store2; - ~AStore2WithReconnect() override = default; - void SetUp() override - { - Core::File(Path).Destroy(); - // File is destroyed - - Core::SystemInfo::SetEnvironment(_T("PERSISTENTSTORE_PATH"), Path); - Core::SystemInfo::SetEnvironment(_T("PERSISTENTSTORE_MAXSIZE"), std::to_string(MaxSize)); - Core::SystemInfo::SetEnvironment(_T("PERSISTENTSTORE_MAXVALUE"), std::to_string(MaxValue)); - Core::SystemInfo::SetEnvironment(_T("PERSISTENTSTORE_LIMIT"), std::to_string(Limit)); - store2 = Core::Service>>::Create(); - } - void TearDown() override - { - store2->Release(); - } -}; - -TEST_F(AStore2WithReconnect, SetsValueAfterFileDestroy) -{ - Core::File(Path).Destroy(); - // File is destroyed - - EXPECT_THAT(store2->SetValue(Exchange::IStore2::ScopeType::DEVICE, "ns1", "key1", "value1", 0), Eq(Core::ERROR_NONE)); - string value; - uint32_t ttl; - EXPECT_THAT(store2->GetValue(Exchange::IStore2::ScopeType::DEVICE, "ns1", "key1", value, ttl), Eq(Core::ERROR_NONE)); - EXPECT_THAT(value, Eq("value1")); - EXPECT_THAT(ttl, Eq(0)); -} diff --git a/PersistentStore/sqlite/l1test/StoreCacheTest.cpp b/PersistentStore/sqlite/l1test/StoreCacheTest.cpp index 6b6736ebc8..5d2af5fd1d 100644 --- a/PersistentStore/sqlite/l1test/StoreCacheTest.cpp +++ b/PersistentStore/sqlite/l1test/StoreCacheTest.cpp @@ -1,42 +1,29 @@ #include #include -#include "../Handle.h" -#include "../StoreCacheType.h" - -using namespace WPEFramework; -using namespace WPEFramework::Plugin; +#include "../StoreCache.h" using ::testing::Eq; using ::testing::Test; +using ::WPEFramework::Exchange::IStoreCache; +using ::WPEFramework::Plugin::Sqlite::StoreCache; -const std::string Path = "/tmp/sqlite/l1test/storecachetest"; -const uint32_t MaxSize = 100; -const uint32_t MaxValue = 10; -const uint32_t Limit = 50; +const auto kPath = "/tmp/persistentstore/sqlite/l1test/storecachetest"; class AStoreCache : public Test { protected: - Exchange::IStoreCache* cache; - ~AStoreCache() override = default; - void SetUp() override + IStoreCache* cache; + AStoreCache() + : cache(WPEFramework::Core::Service::Create(kPath)) { - Core::File(Path).Destroy(); - // File is destroyed - - Core::SystemInfo::SetEnvironment(_T("PERSISTENTSTORE_PATH"), Path); - Core::SystemInfo::SetEnvironment(_T("PERSISTENTSTORE_MAXSIZE"), std::to_string(MaxSize)); - Core::SystemInfo::SetEnvironment(_T("PERSISTENTSTORE_MAXVALUE"), std::to_string(MaxValue)); - Core::SystemInfo::SetEnvironment(_T("PERSISTENTSTORE_LIMIT"), std::to_string(Limit)); - cache = Core::Service>::Create(); } - void TearDown() override + ~AStoreCache() override { cache->Release(); } }; -TEST_F(AStoreCache, FlushesCacheWithoutError) +TEST_F(AStoreCache, FlushesCache) { - EXPECT_THAT(cache->FlushCache(), Eq(Core::ERROR_NONE)); + EXPECT_THAT(cache->FlushCache(), Eq(WPEFramework::Core::ERROR_NONE)); } diff --git a/PersistentStore/sqlite/l1test/StoreInspectorTest.cpp b/PersistentStore/sqlite/l1test/StoreInspectorTest.cpp index 154730d41e..ec3dd3866f 100644 --- a/PersistentStore/sqlite/l1test/StoreInspectorTest.cpp +++ b/PersistentStore/sqlite/l1test/StoreInspectorTest.cpp @@ -1,129 +1,96 @@ #include #include -#include "../Handle.h" -#include "../Store2Type.h" -#include "../StoreInspectorType.h" - -using namespace WPEFramework; -using namespace WPEFramework::Plugin; +#include "../Store2.h" +#include "../StoreInspector.h" using ::testing::Eq; using ::testing::IsFalse; using ::testing::IsTrue; using ::testing::NotNull; using ::testing::Test; +using ::WPEFramework::Exchange::IStore2; +using ::WPEFramework::Exchange::IStoreInspector; +using ::WPEFramework::Plugin::Sqlite::Store2; +using ::WPEFramework::Plugin::Sqlite::StoreInspector; +using ::WPEFramework::RPC::IStringIterator; -const std::string Path = "/tmp/sqlite/l1test/storeinspectortest"; -const uint32_t MaxSize = 100; -const uint32_t MaxValue = 10; -const uint32_t Limit = 50; +const auto kPath = "/tmp/persistentstore/sqlite/l1test/storeinspectortest"; +const auto kMaxSize = 100; +const auto kMaxValue = 10; +const auto kLimit = 50; +const auto kValue = "value_1"; +const auto kKey = "key_1"; +const auto kAppId = "app_id_1"; +const auto kNoTtl = 0; +const auto kScope = IStoreInspector::ScopeType::DEVICE; +const auto kUnknown = "unknown"; class AStoreInspector : public Test { protected: - Exchange::IStoreInspector* inspector; - ~AStoreInspector() override = default; - void SetUp() override + IStoreInspector* inspector; + AStoreInspector() + : inspector(WPEFramework::Core::Service::Create(kPath)) { - Core::File(Path).Destroy(); - // File is destroyed - - Core::SystemInfo::SetEnvironment(_T("PERSISTENTSTORE_PATH"), Path); - Core::SystemInfo::SetEnvironment(_T("PERSISTENTSTORE_MAXSIZE"), std::to_string(MaxSize)); - Core::SystemInfo::SetEnvironment(_T("PERSISTENTSTORE_MAXVALUE"), std::to_string(MaxValue)); - Core::SystemInfo::SetEnvironment(_T("PERSISTENTSTORE_LIMIT"), std::to_string(Limit)); - inspector = Core::Service>::Create(); } - void TearDown() override + ~AStoreInspector() override { inspector->Release(); } }; -TEST_F(AStoreInspector, GetsKeysForUnknownNamespaceWithoutError) +TEST_F(AStoreInspector, GetsKeysWhenNamespaceUnknown) { - RPC::IStringIterator* it; - ASSERT_THAT(inspector->GetKeys(Exchange::IStore2::ScopeType::DEVICE, "x", it), Eq(Core::ERROR_NONE)); + IStringIterator* it; + ASSERT_THAT(inspector->GetKeys(kScope, kUnknown, it), Eq(WPEFramework::Core::ERROR_NONE)); ASSERT_THAT(it, NotNull()); string element; EXPECT_THAT(it->Next(element), IsFalse()); it->Release(); } -TEST_F(AStoreInspector, GetsNamespacesWhenEmptyWithoutError) +TEST_F(AStoreInspector, GetsKeys) { - RPC::IStringIterator* it; - ASSERT_THAT(inspector->GetNamespaces(Exchange::IStore2::ScopeType::DEVICE, it), Eq(Core::ERROR_NONE)); + auto store2 = WPEFramework::Core::Service::Create(kPath, kMaxSize, kMaxValue, kLimit); + ASSERT_THAT(store2->SetValue(kScope, kAppId, kKey, kValue, kNoTtl), Eq(WPEFramework::Core::ERROR_NONE)); + store2->Release(); + IStringIterator* it; + ASSERT_THAT(inspector->GetKeys(kScope, kAppId, it), Eq(WPEFramework::Core::ERROR_NONE)); ASSERT_THAT(it, NotNull()); string element; - ASSERT_THAT(it->Next(element), IsFalse()); - it->Release(); -} - -TEST_F(AStoreInspector, GetsStorageSizesWhenEmptyWithoutError) -{ - Exchange::IStoreInspector::INamespaceSizeIterator* it; - ASSERT_THAT(inspector->GetStorageSizes(Exchange::IStore2::ScopeType::DEVICE, it), Eq(Core::ERROR_NONE)); - ASSERT_THAT(it, NotNull()); - Exchange::IStoreInspector::NamespaceSize element; - ASSERT_THAT(it->Next(element), IsFalse()); - it->Release(); -} - -class AStoreInspectorWithValues : public AStoreInspector { -protected: - ~AStoreInspectorWithValues() override = default; - void SetUp() override - { - AStoreInspector::SetUp(); - auto store = Core::Service>::Create(); - ASSERT_THAT(store->SetValue(Exchange::IStore2::ScopeType::DEVICE, "ns1", "key1", "value1", 0), Eq(Core::ERROR_NONE)); - ASSERT_THAT(store->SetValue(Exchange::IStore2::ScopeType::DEVICE, "ns1", "key2", "value2", 0), Eq(Core::ERROR_NONE)); - ASSERT_THAT(store->SetValue(Exchange::IStore2::ScopeType::DEVICE, "ns2", "key1", "value1", 0), Eq(Core::ERROR_NONE)); - store->Release(); - } -}; - -TEST_F(AStoreInspectorWithValues, GetsKeys) -{ - RPC::IStringIterator* it; - ASSERT_THAT(inspector->GetKeys(Exchange::IStore2::ScopeType::DEVICE, "ns1", it), Eq(Core::ERROR_NONE)); - ASSERT_THAT(it, NotNull()); - string element; - ASSERT_THAT(it->Next(element), IsTrue()); - EXPECT_THAT(element, Eq("key1")); ASSERT_THAT(it->Next(element), IsTrue()); - EXPECT_THAT(element, Eq("key2")); + EXPECT_THAT(element, Eq(kKey)); EXPECT_THAT(it->Next(element), IsFalse()); it->Release(); } -TEST_F(AStoreInspectorWithValues, GetsNamespaces) +TEST_F(AStoreInspector, GetsNamespaces) { - RPC::IStringIterator* it; - ASSERT_THAT(inspector->GetNamespaces(Exchange::IStore2::ScopeType::DEVICE, it), Eq(Core::ERROR_NONE)); + auto store2 = WPEFramework::Core::Service::Create(kPath, kMaxSize, kMaxValue, kLimit); + ASSERT_THAT(store2->SetValue(kScope, kAppId, kKey, kValue, kNoTtl), Eq(WPEFramework::Core::ERROR_NONE)); + store2->Release(); + IStringIterator* it; + ASSERT_THAT(inspector->GetNamespaces(kScope, it), Eq(WPEFramework::Core::ERROR_NONE)); ASSERT_THAT(it, NotNull()); string element; ASSERT_THAT(it->Next(element), IsTrue()); - EXPECT_THAT(element, Eq("ns1")); - ASSERT_THAT(it->Next(element), IsTrue()); - EXPECT_THAT(element, Eq("ns2")); + EXPECT_THAT(element, Eq(kAppId)); EXPECT_THAT(it->Next(element), IsFalse()); it->Release(); } -TEST_F(AStoreInspectorWithValues, GetsStorageSizes) +TEST_F(AStoreInspector, GetsStorageSizes) { - Exchange::IStoreInspector::INamespaceSizeIterator* it; - ASSERT_THAT(inspector->GetStorageSizes(Exchange::IStore2::ScopeType::DEVICE, it), Eq(Core::ERROR_NONE)); + auto store2 = WPEFramework::Core::Service::Create(kPath, kMaxSize, kMaxValue, kLimit); + ASSERT_THAT(store2->SetValue(kScope, kAppId, kKey, kValue, kNoTtl), Eq(WPEFramework::Core::ERROR_NONE)); + store2->Release(); + IStoreInspector::INamespaceSizeIterator* it; + ASSERT_THAT(inspector->GetStorageSizes(kScope, it), Eq(WPEFramework::Core::ERROR_NONE)); ASSERT_THAT(it, NotNull()); - Exchange::IStoreInspector::NamespaceSize element; - ASSERT_THAT(it->Next(element), IsTrue()); - EXPECT_THAT(element.ns, Eq("ns1")); - EXPECT_THAT(element.size, Eq(20)); + IStoreInspector::NamespaceSize element; ASSERT_THAT(it->Next(element), IsTrue()); - EXPECT_THAT(element.ns, Eq("ns2")); - EXPECT_THAT(element.size, Eq(10)); + EXPECT_THAT(element.ns, Eq(kAppId)); + EXPECT_THAT(element.size, Eq(strlen(kKey) + strlen(kValue))); EXPECT_THAT(it->Next(element), IsFalse()); it->Release(); } diff --git a/PersistentStore/sqlite/l1test/StoreLimitTest.cpp b/PersistentStore/sqlite/l1test/StoreLimitTest.cpp index 7e62e7a51c..49409e91d3 100644 --- a/PersistentStore/sqlite/l1test/StoreLimitTest.cpp +++ b/PersistentStore/sqlite/l1test/StoreLimitTest.cpp @@ -1,98 +1,112 @@ #include #include -#include "../Handle.h" -#include "../Store2Type.h" -#include "../StoreLimitType.h" - -using namespace WPEFramework; -using namespace WPEFramework::Plugin; +#include "../Store2.h" +#include "../StoreLimit.h" using ::testing::Eq; using ::testing::Test; - -const std::string Path = "/tmp/sqlite/l1test/storelimittest"; -const uint32_t MaxSize = 100; -const uint32_t MaxValue = 10; -const uint32_t Limit = 50; +using ::WPEFramework::Exchange::IStore2; +using ::WPEFramework::Exchange::IStoreLimit; +using ::WPEFramework::Plugin::Sqlite::Store2; +using ::WPEFramework::Plugin::Sqlite::StoreLimit; + +const auto kPath = "/tmp/persistentstore/sqlite/l1test/storelimittest"; +const auto kMaxSize = 100; +const auto kMaxValue = 10; +const auto kLimit = 50; +const auto kValue = "value_1"; +const auto kKey = "key_1"; +const auto kAppId = "app_id_1"; +const auto kNoTtl = 0; +const auto kScope = IStoreLimit::ScopeType::DEVICE; +const auto kEmpty = ""; +const auto kOversize = "this is too large"; +const auto kUnknown = "unknown"; +const auto kLimit20 = 20; +const auto kLimit30 = 30; +const auto kLimit40 = 40; class AStoreLimit : public Test { protected: - Exchange::IStoreLimit* limit; - ~AStoreLimit() override = default; - void SetUp() override + IStoreLimit* limit; + AStoreLimit() + : limit(WPEFramework::Core::Service::Create(kPath, kMaxSize, kMaxValue)) { - Core::File(Path).Destroy(); - // File is destroyed - - Core::SystemInfo::SetEnvironment(_T("PERSISTENTSTORE_PATH"), Path); - Core::SystemInfo::SetEnvironment(_T("PERSISTENTSTORE_MAXSIZE"), std::to_string(MaxSize)); - Core::SystemInfo::SetEnvironment(_T("PERSISTENTSTORE_MAXVALUE"), std::to_string(MaxValue)); - Core::SystemInfo::SetEnvironment(_T("PERSISTENTSTORE_LIMIT"), std::to_string(Limit)); - limit = Core::Service>::Create(); } - void TearDown() override + ~AStoreLimit() override { limit->Release(); } }; -TEST_F(AStoreLimit, SetsLimitForUnknownNamespaceWithoutError) +TEST_F(AStoreLimit, DoesNotGetNamespaceStorageLimitWhenNamespaceUnknown) { - EXPECT_THAT(limit->SetNamespaceStorageLimit(Exchange::IStore2::ScopeType::DEVICE, "x", 10), Eq(Core::ERROR_NONE)); + uint32_t value; + EXPECT_THAT(limit->GetNamespaceStorageLimit(kScope, kUnknown, value), Eq(WPEFramework::Core::ERROR_NOT_EXIST)); } -TEST_F(AStoreLimit, DoesNotGetLimitForUnknownNamespace) +TEST_F(AStoreLimit, DoesNotSetNamespaceStorageLimitWhenNamespaceEmpty) { - uint32_t value; - EXPECT_THAT(limit->GetNamespaceStorageLimit(Exchange::IStore2::ScopeType::DEVICE, "x", value), Eq(Core::ERROR_NOT_EXIST)); + EXPECT_THAT(limit->SetNamespaceStorageLimit(kScope, kEmpty, kLimit20), Eq(WPEFramework::Core::ERROR_INVALID_INPUT_LENGTH)); } -TEST_F(AStoreLimit, DoesNotSetLimitForTooLargeNamespaceName) +TEST_F(AStoreLimit, DoesNotSetNamespaceStorageLimitWhenNamespaceOversize) { - EXPECT_THAT(limit->SetNamespaceStorageLimit(Exchange::IStore2::ScopeType::DEVICE, "this is too large", 10), Eq(Core::ERROR_INVALID_INPUT_LENGTH)); + EXPECT_THAT(limit->SetNamespaceStorageLimit(kScope, kOversize, kLimit20), Eq(WPEFramework::Core::ERROR_INVALID_INPUT_LENGTH)); } -TEST_F(AStoreLimit, DoesNotSetLimitWhenAtMaxSize) +TEST_F(AStoreLimit, SetsNamespaceStorageLimit) { - for (int i = 0; i < 27; i++) { - EXPECT_THAT(limit->SetNamespaceStorageLimit(Exchange::IStore2::ScopeType::DEVICE, "ns" + std::to_string(i), 10), Eq(Core::ERROR_NONE)); - } - EXPECT_THAT(limit->SetNamespaceStorageLimit(Exchange::IStore2::ScopeType::DEVICE, "ns", 10), Eq(Core::ERROR_NONE)); - // Size is 100 - - EXPECT_THAT(limit->SetNamespaceStorageLimit(Exchange::IStore2::ScopeType::DEVICE, "x", 10), Eq(Core::ERROR_INVALID_INPUT_LENGTH)); + ASSERT_THAT(limit->SetNamespaceStorageLimit(kScope, kAppId, kLimit20), Eq(WPEFramework::Core::ERROR_NONE)); + uint32_t value; + ASSERT_THAT(limit->GetNamespaceStorageLimit(kScope, kAppId, value), Eq(WPEFramework::Core::ERROR_NONE)); + EXPECT_THAT(value, Eq(kLimit20)); } -TEST_F(AStoreLimit, GetsDefaultLimitForExistingNamespace) +TEST_F(AStoreLimit, SetsNamespaceStorageLimitWhenAlreadySet) { - auto store2 = Core::Service>::Create(); - ASSERT_THAT(store2->SetValue(Exchange::IStore2::ScopeType::DEVICE, "ns1", "key1", "value1", 0), Eq(Core::ERROR_NONE)); - store2->Release(); - // Namespace added - + ASSERT_THAT(limit->SetNamespaceStorageLimit(kScope, kAppId, kLimit30), Eq(WPEFramework::Core::ERROR_NONE)); uint32_t value; - EXPECT_THAT(limit->GetNamespaceStorageLimit(Exchange::IStore2::ScopeType::DEVICE, "ns1", value), Eq(Core::ERROR_NONE)); - EXPECT_THAT(value, Eq(Limit)); + ASSERT_THAT(limit->GetNamespaceStorageLimit(kScope, kAppId, value), Eq(WPEFramework::Core::ERROR_NONE)); + EXPECT_THAT(value, Eq(kLimit30)); + ASSERT_THAT(limit->SetNamespaceStorageLimit(kScope, kAppId, kLimit40), Eq(WPEFramework::Core::ERROR_NONE)); + ASSERT_THAT(limit->GetNamespaceStorageLimit(kScope, kAppId, value), Eq(WPEFramework::Core::ERROR_NONE)); + EXPECT_THAT(value, Eq(kLimit40)); } -TEST_F(AStoreLimit, GetsLimitThatWasSet) +TEST_F(AStoreLimit, DoesNotSetNamespaceStorageLimitWhenReachedMaxSize) { - ASSERT_THAT(limit->SetNamespaceStorageLimit(Exchange::IStore2::ScopeType::DEVICE, "ns1", 30), Eq(Core::ERROR_NONE)); - // Namespace limit set - - uint32_t value; - EXPECT_THAT(limit->GetNamespaceStorageLimit(Exchange::IStore2::ScopeType::DEVICE, "ns1", value), Eq(Core::ERROR_NONE)); - EXPECT_THAT(value, Eq(30)); + auto store2 = WPEFramework::Core::Service::Create(kPath, kMaxSize, kMaxValue, kLimit); + ASSERT_THAT(store2->DeleteNamespace(kScope, kAppId), Eq(WPEFramework::Core::ERROR_NONE)); + ASSERT_THAT(store2->SetValue(kScope, "8InMXXU4hM", "YWKN74ODMf", "N0ed2C2h4n", kNoTtl), Eq(WPEFramework::Core::ERROR_NONE)); + ASSERT_THAT(store2->SetValue(kScope, "XhrICnuerw", "jPKODBDk5K", "d3BarkA5xF", kNoTtl), Eq(WPEFramework::Core::ERROR_NONE)); + ASSERT_THAT(store2->SetValue(kScope, "WNeBknDDI2", "GC96ZN6Fuq", "IBF2E1MLQh", kNoTtl), Eq(WPEFramework::Core::ERROR_NONE)); + ASSERT_THAT(store2->SetValue(kScope, "V92", "R1R", "rHk", kNoTtl), Eq(WPEFramework::Core::ERROR_NONE)); + EXPECT_THAT(limit->SetNamespaceStorageLimit(kScope, kAppId, kLimit), Eq(WPEFramework::Core::ERROR_INVALID_INPUT_LENGTH)); + EXPECT_THAT(store2->DeleteNamespace(kScope, "8InMXXU4hM"), Eq(WPEFramework::Core::ERROR_NONE)); + EXPECT_THAT(store2->DeleteNamespace(kScope, "XhrICnuerw"), Eq(WPEFramework::Core::ERROR_NONE)); + EXPECT_THAT(store2->DeleteNamespace(kScope, "WNeBknDDI2"), Eq(WPEFramework::Core::ERROR_NONE)); + EXPECT_THAT(store2->DeleteNamespace(kScope, "V92"), Eq(WPEFramework::Core::ERROR_NONE)); + store2->Release(); } -TEST_F(AStoreLimit, UpdatesLimitThatWasSet) +TEST_F(AStoreLimit, EnforcesSetValueToFailWhenReachedDefaultLimit) { - ASSERT_THAT(limit->SetNamespaceStorageLimit(Exchange::IStore2::ScopeType::DEVICE, "ns1", 30), Eq(Core::ERROR_NONE)); - // Namespace limit set + auto store2 = WPEFramework::Core::Service::Create(kPath, kMaxSize, kMaxValue, kLimit); + ASSERT_THAT(store2->DeleteNamespace(kScope, kAppId), Eq(WPEFramework::Core::ERROR_NONE)); + ASSERT_THAT(store2->SetValue(kScope, kAppId, "YWKN74ODMf", "N0ed2C2h4n", kNoTtl), Eq(WPEFramework::Core::ERROR_NONE)); + ASSERT_THAT(store2->SetValue(kScope, kAppId, "jPKODBDk5K", "d3BarkA5xF", kNoTtl), Eq(WPEFramework::Core::ERROR_NONE)); + ASSERT_THAT(store2->SetValue(kScope, kAppId, kKey, kValue, kNoTtl), Eq(WPEFramework::Core::ERROR_INVALID_INPUT_LENGTH)); + store2->Release(); +} - ASSERT_THAT(limit->SetNamespaceStorageLimit(Exchange::IStore2::ScopeType::DEVICE, "ns1", 40), Eq(Core::ERROR_NONE)); - uint32_t value; - EXPECT_THAT(limit->GetNamespaceStorageLimit(Exchange::IStore2::ScopeType::DEVICE, "ns1", value), Eq(Core::ERROR_NONE)); - EXPECT_THAT(value, Eq(40)); +TEST_F(AStoreLimit, EnforcesSetValueToFailWhenReachedLimit) +{ + auto store2 = WPEFramework::Core::Service::Create(kPath, kMaxSize, kMaxValue, kLimit); + ASSERT_THAT(store2->DeleteNamespace(kScope, kAppId), Eq(WPEFramework::Core::ERROR_NONE)); + EXPECT_THAT(limit->SetNamespaceStorageLimit(kScope, kAppId, kLimit20), Eq(WPEFramework::Core::ERROR_NONE)); + ASSERT_THAT(store2->SetValue(kScope, kAppId, "YWKN74ODMf", "N0ed2C2h4n", kNoTtl), Eq(WPEFramework::Core::ERROR_NONE)); + ASSERT_THAT(store2->SetValue(kScope, kAppId, kKey, kValue, kNoTtl), Eq(WPEFramework::Core::ERROR_INVALID_INPUT_LENGTH)); + store2->Release(); }