220 lines
7.6 KiB
C++
220 lines
7.6 KiB
C++
/*
|
|
* Copyright 2018 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 <ctime>
|
|
#include <thread>
|
|
|
|
#include <gtest/gtest.h>
|
|
|
|
#include "AVSCommon/Utils/Timing/SafeCTimeAccess.h"
|
|
|
|
namespace alexaClientSDK {
|
|
namespace avsCommon {
|
|
namespace utils {
|
|
namespace timing {
|
|
namespace test {
|
|
|
|
/// A bound on the upper time to check.
|
|
const time_t LARGE_TIME_VALUE = (0x1 << 30) - 1;
|
|
|
|
/// Test to verify that getGmtime returns failure if a nullptr is passed for the result variable.
|
|
TEST(SafeCTimeAccessTest, getGmtimeNullReturnValue) {
|
|
auto safeCTimeAccess = SafeCTimeAccess::instance();
|
|
ASSERT_FALSE(safeCTimeAccess->getGmtime(0, nullptr));
|
|
}
|
|
|
|
/// Test to verify that getLocaltime returns failure if a nullptr is passed for the result variable.
|
|
TEST(SafeCTimeAccessTest, getLocaltimeNullReturnValue) {
|
|
auto safeCTimeAccess = SafeCTimeAccess::instance();
|
|
ASSERT_FALSE(safeCTimeAccess->getLocaltime(0, nullptr));
|
|
}
|
|
|
|
/**
|
|
* Utility function to check to see if two std::tm objects are equal.
|
|
*
|
|
* @param a One of the structures to check.
|
|
* @param b The other structure to check.
|
|
*/
|
|
static void checkTm(const std::tm& a, const std::tm& b) {
|
|
EXPECT_EQ(a.tm_sec, b.tm_sec);
|
|
EXPECT_EQ(a.tm_min, b.tm_min);
|
|
EXPECT_EQ(a.tm_hour, b.tm_hour);
|
|
EXPECT_EQ(a.tm_mday, b.tm_mday);
|
|
EXPECT_EQ(a.tm_mon, b.tm_mon);
|
|
EXPECT_EQ(a.tm_year, b.tm_year);
|
|
EXPECT_EQ(a.tm_wday, b.tm_wday);
|
|
EXPECT_EQ(a.tm_yday, b.tm_yday);
|
|
EXPECT_EQ(a.tm_isdst, b.tm_isdst);
|
|
}
|
|
|
|
/**
|
|
* Helper function to run through the test cases for getGmtime testing.
|
|
*
|
|
* @param expected The std::tm structure string that getGmtime should generate.
|
|
* @param t The time_t to convert.
|
|
*/
|
|
static void testGmtimeHelper(const std::tm& expected, const time_t& t) {
|
|
auto safeCTimeAccess = SafeCTimeAccess::instance();
|
|
std::tm result;
|
|
EXPECT_TRUE(safeCTimeAccess->getGmtime(t, &result));
|
|
checkTm(expected, result);
|
|
}
|
|
|
|
/**
|
|
* Helper function to run through the test cases for getLocaltime testing.
|
|
*
|
|
* @param expected The std::tm structure string that getLocaltime should generate.
|
|
* @param t The time_t to convert.
|
|
*/
|
|
static void testLocaltimeHelper(const std::tm& expected, const time_t& t) {
|
|
auto safeCTimeAccess = SafeCTimeAccess::instance();
|
|
std::tm result;
|
|
EXPECT_TRUE(safeCTimeAccess->getLocaltime(t, &result));
|
|
checkTm(expected, result);
|
|
}
|
|
|
|
/// Test to verify that getGmtime returns the correct calendar date for the Unix epoch.
|
|
TEST(SafeCTimeAccessTest, getGmtimeAtTheEpoch) {
|
|
std::tm epoch;
|
|
epoch.tm_sec = 0;
|
|
epoch.tm_min = 0;
|
|
epoch.tm_hour = 0;
|
|
epoch.tm_mday = 1;
|
|
epoch.tm_mon = 0;
|
|
epoch.tm_year = 70;
|
|
epoch.tm_wday = 4;
|
|
epoch.tm_yday = 0;
|
|
epoch.tm_isdst = 0;
|
|
testGmtimeHelper(epoch, 0);
|
|
}
|
|
|
|
/// Test to verify that getGmtime returns the same calendar date as std::gmtime.
|
|
TEST(SafeCTimeAccessTest, getGmtime) {
|
|
for (time_t t = 0; t < LARGE_TIME_VALUE; t = 2 * (t + 1)) {
|
|
auto gmtimeResult = std::gmtime(&t);
|
|
ASSERT_NE(nullptr, gmtimeResult);
|
|
testGmtimeHelper(*gmtimeResult, t);
|
|
}
|
|
}
|
|
|
|
/// Test to verify that getLocaltime returns the same calendar date as std::localtime.
|
|
TEST(SafeCTimeAccessTest, getLocaltime) {
|
|
for (time_t t = 0; t < LARGE_TIME_VALUE; t = 2 * (t + 1)) {
|
|
auto localtimeResult = std::localtime(&t);
|
|
ASSERT_NE(nullptr, localtimeResult);
|
|
testLocaltimeHelper(*localtimeResult, t);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* The test code for SafeCTimeTest::getGmtime and SafeCTimeTest::getLocaltime is almost identical, this allows switching
|
|
* between them.
|
|
*/
|
|
enum class TestType { GMTIME, LOCALTIME };
|
|
|
|
/// Mutex to guard access to the results vector.
|
|
static std::mutex g_resultsLock;
|
|
|
|
/**
|
|
* Function that accesses the safe time functions that is called from many threads. We call the function in a tight
|
|
* loop with no lock and save the results to be checked later.
|
|
*
|
|
* @param startingSeed This is used to make sure that different time values are checked.
|
|
* @param type The flavor of testing to perform. Can be GMTIME or LOCALTIME.
|
|
* @param results A vector where the results are appended to be checked later.
|
|
*/
|
|
static void callSafeCTimeFunction(
|
|
int startingSeed,
|
|
const TestType& type,
|
|
std::vector<std::pair<time_t, std::tm>>* results) {
|
|
auto safeCTimeAccess = SafeCTimeAccess::instance();
|
|
std::vector<std::pair<time_t, std::tm>> internalResults;
|
|
for (int i = 0; i < 4; ++i) {
|
|
for (time_t t = startingSeed; t < LARGE_TIME_VALUE; t = 1.5 * (t + 1)) {
|
|
std::tm result;
|
|
switch (type) {
|
|
case TestType::GMTIME:
|
|
EXPECT_TRUE(safeCTimeAccess->getGmtime(t, &result));
|
|
break;
|
|
case TestType::LOCALTIME:
|
|
EXPECT_TRUE(safeCTimeAccess->getLocaltime(t, &result));
|
|
break;
|
|
default:
|
|
EXPECT_TRUE(false);
|
|
break;
|
|
};
|
|
internalResults.push_back(std::make_pair(t, result));
|
|
}
|
|
}
|
|
|
|
std::lock_guard<std::mutex> lock{g_resultsLock};
|
|
results->insert(std::end(*results), std::begin(internalResults), std::end(internalResults));
|
|
}
|
|
|
|
/**
|
|
* Main function for testing multithreaded access to the safe time functions. This function starts multiple threads
|
|
* that continuously access the time functions in an externally thread-unsafe manner. The results are collected in a
|
|
* single vector, where they are compared against the matching system function.
|
|
*
|
|
* @param type The flavor of testing to perform. Can be GMTIME or LOCALTIME.
|
|
*/
|
|
static void checkSafeCTimeFunction(const TestType& type) {
|
|
std::vector<std::pair<time_t, std::tm>> results;
|
|
const int NUMBER_OF_THREADS = 254;
|
|
std::thread threads[NUMBER_OF_THREADS];
|
|
for (int threadIndex = 0; threadIndex < NUMBER_OF_THREADS; ++threadIndex) {
|
|
threads[threadIndex] = std::thread(callSafeCTimeFunction, threadIndex, type, &results);
|
|
}
|
|
|
|
for (int threadIndex = 0; threadIndex < NUMBER_OF_THREADS; ++threadIndex) {
|
|
threads[threadIndex].join();
|
|
}
|
|
|
|
for (const auto& result : results) {
|
|
std::tm* stdResult;
|
|
switch (type) {
|
|
case TestType::GMTIME:
|
|
stdResult = std::gmtime(&result.first);
|
|
break;
|
|
case TestType::LOCALTIME:
|
|
stdResult = std::localtime(&result.first);
|
|
break;
|
|
default:
|
|
EXPECT_TRUE(false);
|
|
break;
|
|
};
|
|
EXPECT_NE(nullptr, stdResult);
|
|
checkTm(*stdResult, result.second);
|
|
}
|
|
}
|
|
|
|
/// Test to make sure that multithreaded access SafeCTimeAccess::getGmtime is safe.
|
|
TEST(SafeCTimeAccessTest, DISABLED_gmTimeMultithreadedAccess) {
|
|
// TODO: ACSDK-1208 investigate Pi failure
|
|
checkSafeCTimeFunction(TestType::GMTIME);
|
|
}
|
|
|
|
/// Test to make sure that multithreaded access SafeCTimeAccess::getLocaltimetime is safe.
|
|
TEST(SafeCTimeAccessTest, DISABLED_localtimeMultithreadedAccess) {
|
|
// TODO: ACSDK-1208 investigate Pi failure
|
|
checkSafeCTimeFunction(TestType::LOCALTIME);
|
|
}
|
|
|
|
} // namespace test
|
|
} // namespace timing
|
|
} // namespace utils
|
|
} // namespace avsCommon
|
|
} // namespace alexaClientSDK
|