avs-device-sdk/Settings/test/SQLiteDeviceSettingStorageT...

329 lines
10 KiB
C++

/*
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0/
*
* or in the "license" file accompanying this file. This file 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 <fstream>
#include <list>
#include <memory>
#include <sstream>
#include <unordered_map>
#include <vector>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <AVSCommon/Utils/Configuration/ConfigurationNode.h>
#include <Settings/Storage/SQLiteDeviceSettingStorage.h>
namespace alexaClientSDK {
namespace settings {
namespace storage {
namespace test {
using namespace ::testing;
using namespace avsCommon::utils;
using namespace avsCommon::utils::configuration;
using namespace settings;
/// The database file name.
static const std::string TEST_DATABASE_FILE_NAME = "SQLiteDeviceSettingStorageTest.db";
/// The path delimiter used by the OS to identify file locations.
static const std::string PATH_DELIMITER = "/";
/// Test Database file path. Initialized at @c main().
static std::string testDatabase;
/// Test configuration JSON. Initialized at @c main().
static std::string deviceSettingJSON;
/// Error message for when the file already exists.
static std::string fileExistError;
/**
* Checks whether a file exists in the file system.
*
* @param file The file name.
* @return Whether the file exists.
*/
static bool fileExists(const std::string& file) {
std::ifstream dbFile(file);
return dbFile.good();
}
class SQLiteDeviceSettingStorageTest : public ::testing::Test {
public:
/// SetUp before each test case.
void SetUp();
/// TearDown after each test case.
void TearDown();
protected:
/// Open database;
/// @return @c true if it succeed; @c false otherwise.
bool openDB();
/// Cleanup function to close.
void closeDB();
/// The database instance. Protected because it needs to be accessed in test cases.
std::unique_ptr<SQLiteDeviceSettingStorage> m_db;
};
void SQLiteDeviceSettingStorageTest::closeDB() {
if (m_db) {
m_db->close();
}
m_db.reset();
}
bool SQLiteDeviceSettingStorageTest::openDB() {
m_db = SQLiteDeviceSettingStorage::create(ConfigurationNode::getRoot());
EXPECT_THAT(m_db, NotNull());
EXPECT_TRUE(m_db && m_db->open());
return !Test::HasFailure();
}
void SQLiteDeviceSettingStorageTest::SetUp() {
// Initialize Global ConfigurationNode with valid value.
auto json = std::shared_ptr<std::stringstream>(new std::stringstream());
*json << deviceSettingJSON;
std::vector<std::shared_ptr<std::istream>> jsonStream;
jsonStream.push_back(json);
ASSERT_TRUE(ConfigurationNode::initialize(jsonStream));
if (fileExists(testDatabase)) {
remove(testDatabase.c_str());
}
}
void SQLiteDeviceSettingStorageTest::TearDown() {
configuration::ConfigurationNode::uninitialize();
closeDB();
if (fileExists(testDatabase)) {
remove(testDatabase.c_str());
}
}
/**
* Test storing status and values to the database.
*/
TEST_F(SQLiteDeviceSettingStorageTest, insertUpdateSettingValue) {
const std::string key = "AAAA";
const std::string value1 = "BBBBB";
const std::string value2 = "CCCCC";
const SettingStatus localStatus = SettingStatus::LOCAL_CHANGE_IN_PROGRESS;
const SettingStatus notAvailableStatus = SettingStatus::NOT_AVAILABLE;
DeviceSettingStorageInterface::SettingStatusAndValue loadedValue;
// Open the database.
openDB();
// Try loading a non-existent key.
loadedValue = m_db->loadSetting(key);
ASSERT_EQ(loadedValue.first, SettingStatus::NOT_AVAILABLE);
// Store some values.
ASSERT_TRUE(m_db->storeSetting(key, value1, localStatus));
// Reopen database to simulate application restarting.
closeDB();
openDB();
loadedValue = m_db->loadSetting(key);
ASSERT_EQ(loadedValue.first, localStatus);
ASSERT_EQ(loadedValue.second, value1);
// Update the value.
ASSERT_TRUE(m_db->storeSetting(key, value2, localStatus));
// Reopen database to simulate application restarting.
closeDB();
openDB();
loadedValue = m_db->loadSetting(key);
ASSERT_EQ(loadedValue.first, localStatus);
ASSERT_EQ(loadedValue.second, value2);
// Update just the status.
ASSERT_TRUE(m_db->updateSettingStatus(key, notAvailableStatus));
// Reopen database to simulate application restarting.
closeDB();
openDB();
loadedValue = m_db->loadSetting(key);
ASSERT_EQ(loadedValue.first, notAvailableStatus);
ASSERT_EQ(loadedValue.second, value2);
}
/**
* Test that create an entry with non escaped key works.
*/
TEST_F(SQLiteDeviceSettingStorageTest, test_storeSettingWithNonEscapedStringKey) {
const std::string key = R"(non-escaped'\%$#*?!`"key)";
const std::string value = "value2";
const SettingStatus localStatus = SettingStatus::LOCAL_CHANGE_IN_PROGRESS;
DeviceSettingStorageInterface::SettingStatusAndValue loadedValue;
// Open the database.
ASSERT_TRUE(openDB());
// Check if store succeeds.
EXPECT_TRUE(m_db->storeSetting(key, value, localStatus));
loadedValue = m_db->loadSetting(key);
EXPECT_EQ(loadedValue.first, localStatus);
EXPECT_EQ(loadedValue.second, value);
}
/**
* Test that create an entry with non escaped characters works.
*/
TEST_F(SQLiteDeviceSettingStorageTest, test_storeSettingWithNonEscapedStringValue) {
const std::string key = "key";
const std::string value = R"(non-escaped'\%$#*?!`"chars)";
const SettingStatus localStatus = SettingStatus::LOCAL_CHANGE_IN_PROGRESS;
DeviceSettingStorageInterface::SettingStatusAndValue loadedValue;
// Open the database.
ASSERT_TRUE(openDB());
// Check if store succeeds.
EXPECT_TRUE(m_db->storeSetting(key, value, localStatus));
loadedValue = m_db->loadSetting(key);
EXPECT_EQ(loadedValue.first, localStatus);
EXPECT_EQ(loadedValue.second, value);
}
/**
* Test that delete an entry with non escaped key works.
*/
TEST_F(SQLiteDeviceSettingStorageTest, test_deleteSettingWithNonEscapedStringKey) {
const std::string key = R"(non-escaped'\%$#*?!`"key)";
// Open the database and add an entry with the given key.
ASSERT_TRUE(openDB());
ASSERT_TRUE(m_db->storeSetting(key, "value", SettingStatus::NOT_AVAILABLE));
// Check if delete succeeds.
EXPECT_TRUE(m_db->deleteSetting(key));
}
/**
* Test removing an entry from the database.
*/
TEST_F(SQLiteDeviceSettingStorageTest, deleteSetting) {
const std::string key = "AAAA";
const std::string value1 = "BBBBB";
const SettingStatus localStatus = SettingStatus::LOCAL_CHANGE_IN_PROGRESS;
DeviceSettingStorageInterface::SettingStatusAndValue loadedValue;
// Open the database.
openDB();
// Store some values.
ASSERT_TRUE(m_db->storeSetting(key, value1, localStatus));
// Reopen database to simulate application restarting.
closeDB();
openDB();
loadedValue = m_db->loadSetting(key);
ASSERT_EQ(loadedValue.first, localStatus);
ASSERT_EQ(loadedValue.second, value1);
// Delete the setting
m_db->deleteSetting(key);
// Try to retrieve the setting.
loadedValue = m_db->loadSetting(key);
ASSERT_EQ(loadedValue.first, SettingStatus::NOT_AVAILABLE);
}
/**
* Test replace values of multiple rows.
*/
TEST_F(SQLiteDeviceSettingStorageTest, replaceMultipleEntries) {
const std::string key = "AAAA";
const std::string key2 = "KKKK";
const std::string value1 = "BBBBB";
const std::string value2 = "CCCCC";
const std::string value3 = "DDDDD";
const SettingStatus localStatus = SettingStatus::LOCAL_CHANGE_IN_PROGRESS;
const SettingStatus notAvailableStatus = SettingStatus::NOT_AVAILABLE;
DeviceSettingStorageInterface::SettingStatusAndValue loadedValue;
// Open the database.
openDB();
// Try loading a non-existent key.
loadedValue = m_db->loadSetting(key);
ASSERT_EQ(loadedValue.first, SettingStatus::NOT_AVAILABLE);
// Store some values.
// key = value1, localStatus
// key2 = value1, localStatus
ASSERT_TRUE(m_db->storeSetting(key, value1, localStatus));
ASSERT_TRUE(m_db->storeSetting(key2, value1, localStatus));
// Now do multiple replace
const std::vector<std::tuple<std::string, std::string, SettingStatus>> newData = {
{std::make_tuple(key, value2, notAvailableStatus)}, {std::make_tuple(key2, value3, notAvailableStatus)}};
m_db->storeSettings(newData);
loadedValue = m_db->loadSetting(key);
ASSERT_EQ(loadedValue.first, notAvailableStatus);
ASSERT_EQ(loadedValue.second, value2);
loadedValue = m_db->loadSetting(key2);
ASSERT_EQ(loadedValue.first, notAvailableStatus);
ASSERT_EQ(loadedValue.second, value3);
}
} // namespace test
} // namespace storage
} // namespace settings
} // namespace alexaClientSDK
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
if (argc < 2) {
std::cerr << "Usage: " << std::string(argv[0]) << " <absolute path to test database file>" << std::endl;
} else {
alexaClientSDK::settings::storage::test::testDatabase =
std::string(argv[1]) + alexaClientSDK::settings::storage::test::PATH_DELIMITER +
alexaClientSDK::settings::storage::test::TEST_DATABASE_FILE_NAME;
// clang-format off
alexaClientSDK::settings::storage::test::deviceSettingJSON = R"(
{
"deviceSettings" : {
"databaseFilePath":")" + alexaClientSDK::settings::storage::test::testDatabase + R"("
}
}
)";
// clang-format on
alexaClientSDK::settings::storage::test::fileExistError =
"Database File " + alexaClientSDK::settings::storage::test::testDatabase + " already exists.";
return RUN_ALL_TESTS();
}
}