/* * Copyright 2018-2019 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 #include #include #include #include #include #include #include #include #include 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 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(new std::stringstream()); *json << deviceSettingJSON; std::vector> 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> 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]) << " " << 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(); } }