1162 lines
44 KiB
C++
1162 lines
44 KiB
C++
/*
|
|
* Copyright 2010-2022 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
|
|
* that can be found in the LICENSE file.
|
|
*/
|
|
|
|
#include "Clock.hpp"
|
|
|
|
#include <atomic>
|
|
#include <condition_variable>
|
|
#include <mutex>
|
|
#include <shared_mutex>
|
|
#include <tuple>
|
|
#include <type_traits>
|
|
#include <vector>
|
|
|
|
#include "gmock/gmock.h"
|
|
#include "gtest/gtest.h"
|
|
|
|
#include "ClockTestSupport.hpp"
|
|
#include "ScopedThread.hpp"
|
|
#include "TestSupport.hpp"
|
|
|
|
using namespace kotlin;
|
|
|
|
TEST(ClockInternalTest, WaitUntilViaFor_Int_ImmediateOK) {
|
|
using TimePoint = std::chrono::time_point<test_support::manual_clock>;
|
|
testing::StrictMock<testing::MockFunction<TimePoint()>> nowF;
|
|
constexpr auto step = std::chrono::seconds(10);
|
|
constexpr auto rest = std::chrono::seconds(1);
|
|
constexpr auto until = TimePoint() + step + step + rest;
|
|
constexpr int timeoutValue = 42;
|
|
constexpr int okValue = 13;
|
|
testing::StrictMock<testing::MockFunction<int(std::chrono::seconds)>> waitForF;
|
|
|
|
{
|
|
testing::InSequence s;
|
|
EXPECT_CALL(nowF, Call()).WillOnce(testing::Return(TimePoint()));
|
|
EXPECT_CALL(waitForF, Call(step)).WillOnce(testing::Return(okValue));
|
|
}
|
|
auto result = internal::waitUntilViaFor(nowF.AsStdFunction(), step, until, timeoutValue, [&](auto interval) {
|
|
return waitForF.Call(std::chrono::duration_cast<std::chrono::seconds>(interval));
|
|
});
|
|
EXPECT_THAT(result, okValue);
|
|
}
|
|
|
|
TEST(ClockInternalTest, WaitUntilViaFor_Int_EventualOK) {
|
|
using TimePoint = std::chrono::time_point<test_support::manual_clock>;
|
|
testing::StrictMock<testing::MockFunction<TimePoint()>> nowF;
|
|
constexpr auto step = std::chrono::seconds(10);
|
|
constexpr auto rest = std::chrono::seconds(1);
|
|
constexpr auto until = TimePoint() + step + step + rest;
|
|
constexpr int timeoutValue = 42;
|
|
constexpr int okValue = 13;
|
|
testing::StrictMock<testing::MockFunction<int(std::chrono::seconds)>> waitForF;
|
|
|
|
{
|
|
testing::InSequence s;
|
|
EXPECT_CALL(nowF, Call()).WillOnce(testing::Return(TimePoint()));
|
|
EXPECT_CALL(waitForF, Call(step)).WillOnce(testing::Return(timeoutValue));
|
|
EXPECT_CALL(nowF, Call()).WillOnce(testing::Return(TimePoint() + step));
|
|
EXPECT_CALL(waitForF, Call(step)).WillOnce(testing::Return(okValue));
|
|
}
|
|
auto result = internal::waitUntilViaFor(nowF.AsStdFunction(), step, until, timeoutValue, [&](auto interval) {
|
|
return waitForF.Call(std::chrono::duration_cast<std::chrono::seconds>(interval));
|
|
});
|
|
EXPECT_THAT(result, okValue);
|
|
}
|
|
|
|
TEST(ClockInternalTest, WaitUntilViaFor_Int_LastChanceOK) {
|
|
using TimePoint = std::chrono::time_point<test_support::manual_clock>;
|
|
testing::StrictMock<testing::MockFunction<TimePoint()>> nowF;
|
|
constexpr auto step = std::chrono::seconds(10);
|
|
constexpr auto rest = std::chrono::seconds(1);
|
|
constexpr auto until = TimePoint() + step + step + rest;
|
|
constexpr int timeoutValue = 42;
|
|
constexpr int okValue = 13;
|
|
testing::StrictMock<testing::MockFunction<int(std::chrono::seconds)>> waitForF;
|
|
|
|
{
|
|
testing::InSequence s;
|
|
EXPECT_CALL(nowF, Call()).WillOnce(testing::Return(TimePoint()));
|
|
EXPECT_CALL(waitForF, Call(step)).WillOnce(testing::Return(timeoutValue));
|
|
EXPECT_CALL(nowF, Call()).WillOnce(testing::Return(TimePoint() + step));
|
|
EXPECT_CALL(waitForF, Call(step)).WillOnce(testing::Return(timeoutValue));
|
|
EXPECT_CALL(nowF, Call()).WillOnce(testing::Return(TimePoint() + step + step));
|
|
EXPECT_CALL(waitForF, Call(rest)).WillOnce(testing::Return(okValue));
|
|
}
|
|
auto result = internal::waitUntilViaFor(nowF.AsStdFunction(), step, until, timeoutValue, [&](auto interval) {
|
|
return waitForF.Call(std::chrono::duration_cast<std::chrono::seconds>(interval));
|
|
});
|
|
EXPECT_THAT(result, okValue);
|
|
}
|
|
|
|
TEST(ClockInternalTest, WaitUntilViaFor_Int_Timeout) {
|
|
using TimePoint = std::chrono::time_point<test_support::manual_clock>;
|
|
testing::StrictMock<testing::MockFunction<TimePoint()>> nowF;
|
|
constexpr auto step = std::chrono::seconds(10);
|
|
constexpr auto rest = std::chrono::seconds(1);
|
|
constexpr auto until = TimePoint() + step + step + rest;
|
|
constexpr int timeoutValue = 42;
|
|
testing::StrictMock<testing::MockFunction<int(std::chrono::seconds)>> waitForF;
|
|
|
|
{
|
|
testing::InSequence s;
|
|
EXPECT_CALL(nowF, Call()).WillOnce(testing::Return(TimePoint()));
|
|
EXPECT_CALL(waitForF, Call(step)).WillOnce(testing::Return(timeoutValue));
|
|
EXPECT_CALL(nowF, Call()).WillOnce(testing::Return(TimePoint() + step));
|
|
EXPECT_CALL(waitForF, Call(step)).WillOnce(testing::Return(timeoutValue));
|
|
EXPECT_CALL(nowF, Call()).WillOnce(testing::Return(TimePoint() + step + step));
|
|
EXPECT_CALL(waitForF, Call(rest)).WillOnce(testing::Return(timeoutValue));
|
|
EXPECT_CALL(nowF, Call()).WillOnce(testing::Return(TimePoint() + step + step + rest));
|
|
}
|
|
auto result = internal::waitUntilViaFor(nowF.AsStdFunction(), step, until, timeoutValue, [&](auto interval) {
|
|
return waitForF.Call(std::chrono::duration_cast<std::chrono::seconds>(interval));
|
|
});
|
|
EXPECT_THAT(result, timeoutValue);
|
|
}
|
|
|
|
TEST(ClockInternalTest, WaitUntilViaFor_Int_ImmediateTimeout) {
|
|
using TimePoint = std::chrono::time_point<test_support::manual_clock>;
|
|
testing::StrictMock<testing::MockFunction<TimePoint()>> nowF;
|
|
constexpr auto step = std::chrono::seconds(10);
|
|
constexpr auto rest = std::chrono::seconds(1);
|
|
constexpr auto until = TimePoint() + step + step + rest;
|
|
constexpr int timeoutValue = 42;
|
|
testing::StrictMock<testing::MockFunction<int(std::chrono::seconds)>> waitForF;
|
|
|
|
{
|
|
testing::InSequence s;
|
|
EXPECT_CALL(nowF, Call()).WillOnce(testing::Return(TimePoint() + step + step + step));
|
|
}
|
|
auto result = internal::waitUntilViaFor(nowF.AsStdFunction(), step, until, timeoutValue, [&](auto interval) {
|
|
return waitForF.Call(std::chrono::duration_cast<std::chrono::seconds>(interval));
|
|
});
|
|
EXPECT_THAT(result, timeoutValue);
|
|
}
|
|
|
|
TEST(ClockInternalTest, WaitUntilViaFor_Int_ClockJumpTimeout) {
|
|
using TimePoint = std::chrono::time_point<test_support::manual_clock>;
|
|
testing::StrictMock<testing::MockFunction<TimePoint()>> nowF;
|
|
constexpr auto step = std::chrono::seconds(10);
|
|
constexpr auto rest = std::chrono::seconds(1);
|
|
constexpr auto until = TimePoint() + step + step + rest;
|
|
constexpr int timeoutValue = 42;
|
|
testing::StrictMock<testing::MockFunction<int(std::chrono::seconds)>> waitForF;
|
|
|
|
{
|
|
testing::InSequence s;
|
|
EXPECT_CALL(nowF, Call()).WillOnce(testing::Return(TimePoint()));
|
|
EXPECT_CALL(waitForF, Call(step)).WillOnce(testing::Return(timeoutValue));
|
|
// Instead of incrementing by `step`, the clock jumped straight to `until`.
|
|
EXPECT_CALL(nowF, Call()).WillOnce(testing::Return(until));
|
|
}
|
|
auto result = internal::waitUntilViaFor(nowF.AsStdFunction(), step, until, timeoutValue, [&](auto interval) {
|
|
return waitForF.Call(std::chrono::duration_cast<std::chrono::seconds>(interval));
|
|
});
|
|
EXPECT_THAT(result, timeoutValue);
|
|
}
|
|
|
|
TEST(ClockInternalTest, WaitUntilViaFor_Int_NonconformantWaitTimeout) {
|
|
using TimePoint = std::chrono::time_point<test_support::manual_clock>;
|
|
testing::StrictMock<testing::MockFunction<TimePoint()>> nowF;
|
|
constexpr auto step = std::chrono::seconds(10);
|
|
// waitFor non-conformingly waits less than specified.
|
|
constexpr auto actualStep = std::chrono::seconds(7);
|
|
constexpr auto rest = std::chrono::seconds(3);
|
|
constexpr auto until = TimePoint() + step + step + rest;
|
|
constexpr int timeoutValue = 42;
|
|
testing::StrictMock<testing::MockFunction<int(std::chrono::seconds)>> waitForF;
|
|
|
|
{
|
|
testing::InSequence s;
|
|
EXPECT_CALL(nowF, Call()).WillOnce(testing::Return(TimePoint()));
|
|
EXPECT_CALL(waitForF, Call(step)).WillOnce(testing::Return(timeoutValue));
|
|
EXPECT_CALL(nowF, Call()).WillOnce(testing::Return(TimePoint() + actualStep));
|
|
EXPECT_CALL(waitForF, Call(step)).WillOnce(testing::Return(timeoutValue));
|
|
EXPECT_CALL(nowF, Call()).WillOnce(testing::Return(TimePoint() + actualStep + actualStep));
|
|
// Waited 2 * 7 out of 2 * 10 + 3 seconds. 9 seconds left. Will wait only 6 seconds.
|
|
EXPECT_CALL(waitForF, Call(std::chrono::seconds(9))).WillOnce(testing::Return(timeoutValue));
|
|
EXPECT_CALL(nowF, Call()).WillOnce(testing::Return(TimePoint() + actualStep + actualStep + std::chrono::seconds(6)));
|
|
EXPECT_CALL(waitForF, Call(std::chrono::seconds(3))).WillOnce(testing::Return(timeoutValue));
|
|
// Finally waited enough.
|
|
EXPECT_CALL(nowF, Call()).WillOnce(testing::Return(TimePoint() + actualStep + actualStep + std::chrono::seconds(9)));
|
|
}
|
|
auto result = internal::waitUntilViaFor(nowF.AsStdFunction(), step, until, timeoutValue, [&](auto interval) {
|
|
return waitForF.Call(std::chrono::duration_cast<std::chrono::seconds>(interval));
|
|
});
|
|
EXPECT_THAT(result, timeoutValue);
|
|
}
|
|
|
|
TEST(ClockInternalTest, WaitUntilViaFor_Void_Timeout) {
|
|
using TimePoint = std::chrono::time_point<test_support::manual_clock>;
|
|
testing::StrictMock<testing::MockFunction<TimePoint()>> nowF;
|
|
constexpr auto step = std::chrono::seconds(10);
|
|
constexpr auto rest = std::chrono::seconds(1);
|
|
constexpr auto until = TimePoint() + step + step + rest;
|
|
testing::StrictMock<testing::MockFunction<void(std::chrono::seconds)>> waitForF;
|
|
|
|
{
|
|
testing::InSequence s;
|
|
EXPECT_CALL(nowF, Call()).WillOnce(testing::Return(TimePoint()));
|
|
EXPECT_CALL(waitForF, Call(step));
|
|
EXPECT_CALL(nowF, Call()).WillOnce(testing::Return(TimePoint() + step));
|
|
EXPECT_CALL(waitForF, Call(step));
|
|
EXPECT_CALL(nowF, Call()).WillOnce(testing::Return(TimePoint() + step + step));
|
|
EXPECT_CALL(waitForF, Call(rest));
|
|
EXPECT_CALL(nowF, Call()).WillOnce(testing::Return(TimePoint() + step + step + rest));
|
|
}
|
|
internal::waitUntilViaFor(nowF.AsStdFunction(), step, until, [&](auto interval) {
|
|
return waitForF.Call(std::chrono::duration_cast<std::chrono::seconds>(interval));
|
|
});
|
|
}
|
|
|
|
TEST(ClockInternalTest, WaitUntilViaFor_Void_ImmediateTimeout) {
|
|
using TimePoint = std::chrono::time_point<test_support::manual_clock>;
|
|
testing::StrictMock<testing::MockFunction<TimePoint()>> nowF;
|
|
constexpr auto step = std::chrono::seconds(10);
|
|
constexpr auto rest = std::chrono::seconds(1);
|
|
constexpr auto until = TimePoint() + step + step + rest;
|
|
testing::StrictMock<testing::MockFunction<void(std::chrono::seconds)>> waitForF;
|
|
|
|
{
|
|
testing::InSequence s;
|
|
EXPECT_CALL(nowF, Call()).WillOnce(testing::Return(TimePoint() + step + step + step));
|
|
}
|
|
internal::waitUntilViaFor(nowF.AsStdFunction(), step, until, [&](auto interval) {
|
|
return waitForF.Call(std::chrono::duration_cast<std::chrono::seconds>(interval));
|
|
});
|
|
}
|
|
|
|
TEST(ClockInternalTest, WaitUntilViaFor_Void_ClockJumpTimeout) {
|
|
using TimePoint = std::chrono::time_point<test_support::manual_clock>;
|
|
testing::StrictMock<testing::MockFunction<TimePoint()>> nowF;
|
|
constexpr auto step = std::chrono::seconds(10);
|
|
constexpr auto rest = std::chrono::seconds(1);
|
|
constexpr auto until = TimePoint() + step + step + rest;
|
|
testing::StrictMock<testing::MockFunction<void(std::chrono::seconds)>> waitForF;
|
|
|
|
{
|
|
testing::InSequence s;
|
|
EXPECT_CALL(nowF, Call()).WillOnce(testing::Return(TimePoint()));
|
|
EXPECT_CALL(waitForF, Call(step));
|
|
// Instead of incrementing by `step`, the clock jumped straight to `until`.
|
|
EXPECT_CALL(nowF, Call()).WillOnce(testing::Return(until));
|
|
}
|
|
internal::waitUntilViaFor(nowF.AsStdFunction(), step, until, [&](auto interval) {
|
|
return waitForF.Call(std::chrono::duration_cast<std::chrono::seconds>(interval));
|
|
});
|
|
}
|
|
|
|
TEST(ClockInternalTest, WaitUntilViaFor_Void_NonconformantWaitTimeout) {
|
|
using TimePoint = std::chrono::time_point<test_support::manual_clock>;
|
|
testing::StrictMock<testing::MockFunction<TimePoint()>> nowF;
|
|
constexpr auto step = std::chrono::seconds(10);
|
|
// waitFor non-conformingly waits less than specified.
|
|
constexpr auto actualStep = std::chrono::seconds(7);
|
|
constexpr auto rest = std::chrono::seconds(3);
|
|
constexpr auto until = TimePoint() + step + step + rest;
|
|
testing::StrictMock<testing::MockFunction<void(std::chrono::seconds)>> waitForF;
|
|
|
|
{
|
|
testing::InSequence s;
|
|
EXPECT_CALL(nowF, Call()).WillOnce(testing::Return(TimePoint()));
|
|
EXPECT_CALL(waitForF, Call(step));
|
|
EXPECT_CALL(nowF, Call()).WillOnce(testing::Return(TimePoint() + actualStep));
|
|
EXPECT_CALL(waitForF, Call(step));
|
|
EXPECT_CALL(nowF, Call()).WillOnce(testing::Return(TimePoint() + actualStep + actualStep));
|
|
// Waited 2 * 7 out of 2 * 10 + 3 seconds. 9 seconds left. Will wait only 6 seconds.
|
|
EXPECT_CALL(waitForF, Call(std::chrono::seconds(9)));
|
|
EXPECT_CALL(nowF, Call()).WillOnce(testing::Return(TimePoint() + actualStep + actualStep + std::chrono::seconds(6)));
|
|
EXPECT_CALL(waitForF, Call(std::chrono::seconds(3)));
|
|
// Finally waited enough.
|
|
EXPECT_CALL(nowF, Call()).WillOnce(testing::Return(TimePoint() + actualStep + actualStep + std::chrono::seconds(9)));
|
|
}
|
|
internal::waitUntilViaFor(nowF.AsStdFunction(), step, until, [&](auto interval) {
|
|
return waitForF.Call(std::chrono::duration_cast<std::chrono::seconds>(interval));
|
|
});
|
|
}
|
|
|
|
namespace {
|
|
|
|
class ClockTestNames {
|
|
public:
|
|
template <typename T>
|
|
static std::string GetName(int) {
|
|
if constexpr (std::is_same_v<T, kotlin::steady_clock>) {
|
|
return "steady_clock";
|
|
} else if constexpr (std::is_same_v<T, kotlin::test_support::manual_clock>) {
|
|
return "manual_clock";
|
|
} else {
|
|
return "unknown";
|
|
}
|
|
}
|
|
};
|
|
|
|
template <typename Clock>
|
|
struct WaitForPendingImpl {
|
|
void operator()() {}
|
|
};
|
|
|
|
template <>
|
|
struct WaitForPendingImpl<test_support::manual_clock> {
|
|
void operator()() {
|
|
while (!test_support::manual_clock::pending()) {
|
|
}
|
|
}
|
|
};
|
|
|
|
template <typename Clock>
|
|
void waitForPending() {
|
|
WaitForPendingImpl<Clock>()();
|
|
}
|
|
|
|
} // namespace
|
|
|
|
template <typename T>
|
|
class ClockTest : public testing::Test {
|
|
public:
|
|
ClockTest() noexcept { test_support::manual_clock::reset(); }
|
|
};
|
|
|
|
using ClockTestTypes = testing::Types<kotlin::steady_clock, kotlin::test_support::manual_clock>;
|
|
TYPED_TEST_SUITE(ClockTest, ClockTestTypes, ClockTestNames);
|
|
|
|
TYPED_TEST(ClockTest, SleepFor) {
|
|
constexpr auto interval = milliseconds(1);
|
|
auto before = TypeParam::now();
|
|
TypeParam::sleep_for(interval);
|
|
auto after = TypeParam::now();
|
|
EXPECT_THAT(after - before, testing::Ge(interval));
|
|
}
|
|
|
|
TYPED_TEST(ClockTest, SleepUntil) {
|
|
auto until = TypeParam::now() + milliseconds(1);
|
|
TypeParam::sleep_until(until);
|
|
auto after = TypeParam::now();
|
|
EXPECT_THAT(after, testing::Ge(until));
|
|
}
|
|
|
|
TYPED_TEST(ClockTest, CVWaitFor_OK) {
|
|
constexpr auto interval = hours(10);
|
|
std::condition_variable cv;
|
|
std::mutex m;
|
|
bool ok = false;
|
|
std::atomic<bool> go = false;
|
|
ScopedThread thread([&] {
|
|
while (!go.load()) {
|
|
}
|
|
waitForPending<TypeParam>();
|
|
{
|
|
std::unique_lock guard(m);
|
|
ok = true;
|
|
}
|
|
cv.notify_all();
|
|
});
|
|
std::unique_lock guard(m);
|
|
auto before = TypeParam::now();
|
|
go = true;
|
|
auto result = TypeParam::wait_for(cv, guard, interval, [&] { return ok; });
|
|
auto after = TypeParam::now();
|
|
EXPECT_TRUE(result);
|
|
EXPECT_THAT(after - before, testing::Lt(interval));
|
|
}
|
|
|
|
TYPED_TEST(ClockTest, CVWaitFor_Timeout) {
|
|
constexpr auto interval = microseconds(10);
|
|
std::condition_variable cv;
|
|
std::mutex m;
|
|
std::atomic<bool> go = false;
|
|
ScopedThread thread([&] {
|
|
while (!go.load()) {
|
|
}
|
|
waitForPending<TypeParam>();
|
|
TypeParam::sleep_for(interval);
|
|
});
|
|
std::unique_lock guard(m);
|
|
auto before = TypeParam::now();
|
|
go = true;
|
|
auto result = TypeParam::wait_for(cv, guard, interval, [] { return false; });
|
|
auto after = TypeParam::now();
|
|
EXPECT_FALSE(result);
|
|
EXPECT_THAT(after - before, testing::Ge(interval));
|
|
}
|
|
|
|
TYPED_TEST(ClockTest, CVWaitFor_InfiniteTimeout) {
|
|
std::condition_variable cv;
|
|
std::mutex m;
|
|
bool ok = false;
|
|
std::atomic<bool> go = false;
|
|
ScopedThread thread([&] {
|
|
while (!go.load()) {
|
|
}
|
|
waitForPending<TypeParam>();
|
|
// Wait to see if `TypeParam::wait_for` wakes up from timeout.
|
|
TypeParam::sleep_for(milliseconds(1));
|
|
{
|
|
std::unique_lock guard(m);
|
|
ok = true;
|
|
}
|
|
cv.notify_all();
|
|
});
|
|
std::unique_lock guard(m);
|
|
go = true;
|
|
auto result = TypeParam::wait_for(cv, guard, microseconds::max(), [&] { return ok; });
|
|
EXPECT_TRUE(result);
|
|
}
|
|
|
|
TYPED_TEST(ClockTest, CVWaitUntil_OK) {
|
|
std::condition_variable cv;
|
|
std::mutex m;
|
|
bool ok = false;
|
|
std::atomic<bool> go = false;
|
|
ScopedThread thread([&] {
|
|
while (!go.load()) {
|
|
}
|
|
waitForPending<TypeParam>();
|
|
{
|
|
std::unique_lock guard(m);
|
|
ok = true;
|
|
}
|
|
cv.notify_all();
|
|
});
|
|
std::unique_lock guard(m);
|
|
auto until = TypeParam::now() + hours(10);
|
|
go = true;
|
|
auto result = TypeParam::wait_until(cv, guard, until, [&] { return ok; });
|
|
auto after = TypeParam::now();
|
|
EXPECT_TRUE(result);
|
|
EXPECT_THAT(after, testing::Lt(until));
|
|
}
|
|
|
|
TYPED_TEST(ClockTest, CVWaitUntil_Timeout) {
|
|
constexpr auto interval = microseconds(10);
|
|
std::condition_variable cv;
|
|
std::mutex m;
|
|
std::atomic<bool> go = false;
|
|
ScopedThread thread([&] {
|
|
while (!go.load()) {
|
|
}
|
|
waitForPending<TypeParam>();
|
|
TypeParam::sleep_for(interval);
|
|
});
|
|
std::unique_lock guard(m);
|
|
auto until = TypeParam::now() + interval;
|
|
go = true;
|
|
auto result = TypeParam::wait_until(cv, guard, until, [] { return false; });
|
|
auto after = TypeParam::now();
|
|
EXPECT_FALSE(result);
|
|
EXPECT_THAT(after, testing::Ge(until));
|
|
}
|
|
|
|
TYPED_TEST(ClockTest, CVWaitUntil_InfiniteTimeout) {
|
|
std::condition_variable cv;
|
|
std::mutex m;
|
|
bool ok = false;
|
|
std::atomic<bool> go = false;
|
|
ScopedThread thread([&] {
|
|
while (!go.load()) {
|
|
}
|
|
waitForPending<TypeParam>();
|
|
// Wait to see if `TypeParam::wait_until` wakes up from timeout.
|
|
TypeParam::sleep_for(milliseconds(1));
|
|
{
|
|
std::unique_lock guard(m);
|
|
ok = true;
|
|
}
|
|
cv.notify_all();
|
|
});
|
|
std::unique_lock guard(m);
|
|
go = true;
|
|
auto result = TypeParam::wait_until(cv, guard, TypeParam::time_point::max(), [&] { return ok; });
|
|
EXPECT_TRUE(result);
|
|
}
|
|
|
|
TYPED_TEST(ClockTest, CVAnyWaitFor_OK) {
|
|
constexpr auto interval = hours(10);
|
|
std::condition_variable_any cv;
|
|
std::shared_mutex m;
|
|
bool ok = false;
|
|
std::atomic<bool> go = false;
|
|
ScopedThread thread([&] {
|
|
while (!go.load()) {
|
|
}
|
|
waitForPending<TypeParam>();
|
|
{
|
|
std::unique_lock guard(m);
|
|
ok = true;
|
|
}
|
|
cv.notify_all();
|
|
});
|
|
std::unique_lock guard(m);
|
|
auto before = TypeParam::now();
|
|
go = true;
|
|
auto result = TypeParam::wait_for(cv, guard, interval, [&] { return ok; });
|
|
auto after = TypeParam::now();
|
|
EXPECT_TRUE(result);
|
|
EXPECT_THAT(after - before, testing::Lt(interval));
|
|
}
|
|
|
|
TYPED_TEST(ClockTest, CVAnyWaitFor_Timeout) {
|
|
constexpr auto interval = microseconds(10);
|
|
std::condition_variable_any cv;
|
|
std::shared_mutex m;
|
|
std::atomic<bool> go = false;
|
|
ScopedThread thread([&] {
|
|
while (!go.load()) {
|
|
}
|
|
waitForPending<TypeParam>();
|
|
TypeParam::sleep_for(interval);
|
|
});
|
|
std::unique_lock guard(m);
|
|
auto before = TypeParam::now();
|
|
go = true;
|
|
auto result = TypeParam::wait_for(cv, guard, interval, [] { return false; });
|
|
auto after = TypeParam::now();
|
|
EXPECT_FALSE(result);
|
|
EXPECT_THAT(after - before, testing::Ge(interval));
|
|
}
|
|
|
|
TYPED_TEST(ClockTest, CVAnyWaitFor_InfiniteTimeout) {
|
|
std::condition_variable_any cv;
|
|
std::shared_mutex m;
|
|
bool ok = false;
|
|
std::atomic<bool> go = false;
|
|
ScopedThread thread([&] {
|
|
while (!go.load()) {
|
|
}
|
|
waitForPending<TypeParam>();
|
|
// Wait to see if `TypeParam::wait_for` wakes up from timeout.
|
|
TypeParam::sleep_for(milliseconds(1));
|
|
{
|
|
std::unique_lock guard(m);
|
|
ok = true;
|
|
}
|
|
cv.notify_all();
|
|
});
|
|
std::unique_lock guard(m);
|
|
go = true;
|
|
auto result = TypeParam::wait_for(cv, guard, microseconds::max(), [&] { return ok; });
|
|
EXPECT_TRUE(result);
|
|
}
|
|
|
|
TYPED_TEST(ClockTest, CVAnyWaitUntil_OK) {
|
|
std::condition_variable_any cv;
|
|
std::shared_mutex m;
|
|
bool ok = false;
|
|
std::atomic<bool> go = false;
|
|
ScopedThread thread([&] {
|
|
while (!go.load()) {
|
|
}
|
|
waitForPending<TypeParam>();
|
|
{
|
|
std::unique_lock guard(m);
|
|
ok = true;
|
|
}
|
|
cv.notify_all();
|
|
});
|
|
std::unique_lock guard(m);
|
|
auto until = TypeParam::now() + hours(10);
|
|
go = true;
|
|
auto result = TypeParam::wait_until(cv, guard, until, [&] { return ok; });
|
|
auto after = TypeParam::now();
|
|
EXPECT_TRUE(result);
|
|
EXPECT_THAT(after, testing::Lt(until));
|
|
}
|
|
|
|
TYPED_TEST(ClockTest, CVAnyWaitUntil_Timeout) {
|
|
constexpr auto interval = microseconds(10);
|
|
std::condition_variable_any cv;
|
|
std::shared_mutex m;
|
|
std::atomic<bool> go = false;
|
|
ScopedThread thread([&] {
|
|
while (!go.load()) {
|
|
}
|
|
waitForPending<TypeParam>();
|
|
TypeParam::sleep_for(interval);
|
|
});
|
|
std::unique_lock guard(m);
|
|
auto until = TypeParam::now() + interval;
|
|
go = true;
|
|
auto result = TypeParam::wait_until(cv, guard, until, [] { return false; });
|
|
auto after = TypeParam::now();
|
|
EXPECT_FALSE(result);
|
|
EXPECT_THAT(after, testing::Ge(until));
|
|
}
|
|
|
|
TYPED_TEST(ClockTest, CVAnyWaitUntil_InfiniteTimeout) {
|
|
std::condition_variable_any cv;
|
|
std::shared_mutex m;
|
|
bool ok = false;
|
|
std::atomic<bool> go = false;
|
|
ScopedThread thread([&] {
|
|
while (!go.load()) {
|
|
}
|
|
waitForPending<TypeParam>();
|
|
// Wait to see if `TypeParam::wait_until` wakes up from timeout.
|
|
TypeParam::sleep_for(milliseconds(1));
|
|
{
|
|
std::unique_lock guard(m);
|
|
ok = true;
|
|
}
|
|
cv.notify_all();
|
|
});
|
|
std::unique_lock guard(m);
|
|
go = true;
|
|
auto result = TypeParam::wait_until(cv, guard, TypeParam::time_point::max(), [&] { return ok; });
|
|
EXPECT_TRUE(result);
|
|
}
|
|
|
|
TYPED_TEST(ClockTest, FutureWaitFor_OK) {
|
|
constexpr auto interval = hours(10);
|
|
std::promise<int> promise;
|
|
std::future<int> future = promise.get_future();
|
|
std::atomic<bool> go = false;
|
|
ScopedThread thread([&] {
|
|
while (!go.load()) {
|
|
}
|
|
waitForPending<TypeParam>();
|
|
promise.set_value(42);
|
|
});
|
|
auto before = TypeParam::now();
|
|
go = true;
|
|
auto result = TypeParam::wait_for(future, interval);
|
|
auto after = TypeParam::now();
|
|
EXPECT_THAT(result, std::future_status::ready);
|
|
EXPECT_THAT(after - before, testing::Lt(interval));
|
|
}
|
|
|
|
TYPED_TEST(ClockTest, FutureWaitFor_Deferred) {
|
|
constexpr auto interval = hours(10);
|
|
std::future<int> future = std::async(std::launch::deferred, [] { return 42; });
|
|
auto before = TypeParam::now();
|
|
auto result = TypeParam::wait_for(future, interval);
|
|
auto after = TypeParam::now();
|
|
EXPECT_THAT(result, std::future_status::deferred);
|
|
EXPECT_THAT(after - before, testing::Lt(interval));
|
|
}
|
|
|
|
TYPED_TEST(ClockTest, FutureWaitFor_Timeout) {
|
|
constexpr auto interval = microseconds(10);
|
|
std::promise<int> promise;
|
|
std::future<int> future = promise.get_future();
|
|
std::atomic<bool> go = false;
|
|
ScopedThread thread([&] {
|
|
while (!go.load()) {
|
|
}
|
|
waitForPending<TypeParam>();
|
|
TypeParam::sleep_for(interval);
|
|
});
|
|
auto before = TypeParam::now();
|
|
go = true;
|
|
auto result = TypeParam::wait_for(future, interval);
|
|
auto after = TypeParam::now();
|
|
EXPECT_THAT(result, std::future_status::timeout);
|
|
EXPECT_THAT(after - before, testing::Ge(interval));
|
|
}
|
|
|
|
TYPED_TEST(ClockTest, FutureWaitFor_InfiniteTimeout) {
|
|
std::promise<int> promise;
|
|
std::future<int> future = promise.get_future();
|
|
std::atomic<bool> go = false;
|
|
ScopedThread thread([&] {
|
|
while (!go.load()) {
|
|
}
|
|
waitForPending<TypeParam>();
|
|
// Wait to see if `TypeParam::wait_for` wakes up from timeout.
|
|
TypeParam::sleep_for(milliseconds(1));
|
|
promise.set_value(42);
|
|
});
|
|
go = true;
|
|
auto result = TypeParam::wait_for(future, microseconds::max());
|
|
EXPECT_THAT(result, std::future_status::ready);
|
|
}
|
|
|
|
TYPED_TEST(ClockTest, FutureWaitUntil_OK) {
|
|
std::promise<int> promise;
|
|
std::future<int> future = promise.get_future();
|
|
std::atomic<bool> go = false;
|
|
ScopedThread thread([&] {
|
|
while (!go.load()) {
|
|
}
|
|
waitForPending<TypeParam>();
|
|
promise.set_value(42);
|
|
});
|
|
auto until = TypeParam::now() + hours(10);
|
|
go = true;
|
|
auto result = TypeParam::wait_until(future, until);
|
|
auto after = TypeParam::now();
|
|
EXPECT_THAT(result, std::future_status::ready);
|
|
EXPECT_THAT(after, testing::Lt(until));
|
|
}
|
|
|
|
TYPED_TEST(ClockTest, FutureWaitUntil_Deferred) {
|
|
std::future<int> future = std::async(std::launch::deferred, [] { return 42; });
|
|
auto until = TypeParam::now() + hours(10);
|
|
auto result = TypeParam::wait_until(future, until);
|
|
auto after = TypeParam::now();
|
|
EXPECT_THAT(result, std::future_status::deferred);
|
|
EXPECT_THAT(after, testing::Lt(until));
|
|
}
|
|
|
|
TYPED_TEST(ClockTest, FutureWaitUntil_Timeout) {
|
|
constexpr auto interval = microseconds(10);
|
|
std::promise<int> promise;
|
|
std::future<int> future = promise.get_future();
|
|
std::atomic<bool> go = false;
|
|
ScopedThread thread([&] {
|
|
while (!go.load()) {
|
|
}
|
|
waitForPending<TypeParam>();
|
|
TypeParam::sleep_for(interval);
|
|
});
|
|
auto until = TypeParam::now() + interval;
|
|
go = true;
|
|
auto result = TypeParam::wait_until(future, until);
|
|
auto after = TypeParam::now();
|
|
EXPECT_THAT(result, std::future_status::timeout);
|
|
EXPECT_THAT(after, testing::Ge(until));
|
|
}
|
|
|
|
TYPED_TEST(ClockTest, FutureWaitUntil_InfiniteTimeout) {
|
|
std::promise<int> promise;
|
|
std::future<int> future = promise.get_future();
|
|
std::atomic<bool> go = false;
|
|
ScopedThread thread([&] {
|
|
while (!go.load()) {
|
|
}
|
|
waitForPending<TypeParam>();
|
|
// Wait to see if `TypeParam::wait_until` wakes up from timeout.
|
|
TypeParam::sleep_for(milliseconds(1));
|
|
promise.set_value(42);
|
|
});
|
|
go = true;
|
|
auto result = TypeParam::wait_until(future, TypeParam::time_point::max());
|
|
EXPECT_THAT(result, std::future_status::ready);
|
|
}
|
|
|
|
TYPED_TEST(ClockTest, SharedFutureWaitFor_OK) {
|
|
constexpr auto interval = hours(10);
|
|
std::promise<int> promise;
|
|
std::shared_future<int> future = promise.get_future();
|
|
std::atomic<bool> go = false;
|
|
ScopedThread thread([&] {
|
|
while (!go.load()) {
|
|
}
|
|
waitForPending<TypeParam>();
|
|
promise.set_value(42);
|
|
});
|
|
auto before = TypeParam::now();
|
|
go = true;
|
|
auto result = TypeParam::wait_for(future, interval);
|
|
auto after = TypeParam::now();
|
|
EXPECT_THAT(result, std::future_status::ready);
|
|
EXPECT_THAT(after - before, testing::Lt(interval));
|
|
}
|
|
|
|
TYPED_TEST(ClockTest, SharedFutureWaitFor_Deferred) {
|
|
constexpr auto interval = hours(10);
|
|
std::shared_future<int> future = std::async(std::launch::deferred, [] { return 42; });
|
|
auto before = TypeParam::now();
|
|
auto result = TypeParam::wait_for(future, interval);
|
|
auto after = TypeParam::now();
|
|
EXPECT_THAT(result, std::future_status::deferred);
|
|
EXPECT_THAT(after - before, testing::Lt(interval));
|
|
}
|
|
|
|
TYPED_TEST(ClockTest, SharedFutureWaitFor_Timeout) {
|
|
constexpr auto interval = microseconds(10);
|
|
std::promise<int> promise;
|
|
std::shared_future<int> future = promise.get_future();
|
|
std::atomic<bool> go = false;
|
|
ScopedThread thread([&] {
|
|
while (!go.load()) {
|
|
}
|
|
waitForPending<TypeParam>();
|
|
TypeParam::sleep_for(interval);
|
|
});
|
|
auto before = TypeParam::now();
|
|
go = true;
|
|
auto result = TypeParam::wait_for(future, interval);
|
|
auto after = TypeParam::now();
|
|
EXPECT_THAT(result, std::future_status::timeout);
|
|
EXPECT_THAT(after - before, testing::Ge(interval));
|
|
}
|
|
|
|
TYPED_TEST(ClockTest, SharedFutureWaitFor_InfiniteTimeout) {
|
|
std::promise<int> promise;
|
|
std::shared_future<int> future = promise.get_future();
|
|
std::atomic<bool> go = false;
|
|
ScopedThread thread([&] {
|
|
while (!go.load()) {
|
|
}
|
|
waitForPending<TypeParam>();
|
|
// Wait to see if `TypeParam::wait_for` wakes up from timeout.
|
|
TypeParam::sleep_for(milliseconds(1));
|
|
promise.set_value(42);
|
|
});
|
|
go = true;
|
|
auto result = TypeParam::wait_for(future, microseconds::max());
|
|
EXPECT_THAT(result, std::future_status::ready);
|
|
}
|
|
|
|
TYPED_TEST(ClockTest, SharedFutureWaitUntil_OK) {
|
|
std::promise<int> promise;
|
|
std::shared_future<int> future = promise.get_future();
|
|
std::atomic<bool> go = false;
|
|
ScopedThread thread([&] {
|
|
while (!go.load()) {
|
|
}
|
|
waitForPending<TypeParam>();
|
|
promise.set_value(42);
|
|
});
|
|
auto until = TypeParam::now() + hours(10);
|
|
go = true;
|
|
auto result = TypeParam::wait_until(future, until);
|
|
auto after = TypeParam::now();
|
|
EXPECT_THAT(result, std::future_status::ready);
|
|
EXPECT_THAT(after, testing::Lt(until));
|
|
}
|
|
|
|
TYPED_TEST(ClockTest, SharedFutureWaitUntil_Deferred) {
|
|
std::shared_future<int> future = std::async(std::launch::deferred, [] { return 42; });
|
|
auto until = TypeParam::now() + hours(10);
|
|
auto result = TypeParam::wait_until(future, until);
|
|
auto after = TypeParam::now();
|
|
EXPECT_THAT(result, std::future_status::deferred);
|
|
EXPECT_THAT(after, testing::Lt(until));
|
|
}
|
|
|
|
TYPED_TEST(ClockTest, SharedFutureWaitUntil_Timeout) {
|
|
constexpr auto interval = microseconds(10);
|
|
std::promise<int> promise;
|
|
std::shared_future<int> future = promise.get_future();
|
|
std::atomic<bool> go = false;
|
|
ScopedThread thread([&] {
|
|
while (!go.load()) {
|
|
}
|
|
waitForPending<TypeParam>();
|
|
TypeParam::sleep_for(interval);
|
|
});
|
|
auto until = TypeParam::now() + interval;
|
|
go = true;
|
|
auto result = TypeParam::wait_until(future, until);
|
|
auto after = TypeParam::now();
|
|
EXPECT_THAT(result, std::future_status::timeout);
|
|
EXPECT_THAT(after, testing::Ge(until));
|
|
}
|
|
|
|
TYPED_TEST(ClockTest, SharedFutureWaitUntil_InfiniteTimeout) {
|
|
std::promise<int> promise;
|
|
std::shared_future<int> future = promise.get_future();
|
|
std::atomic<bool> go = false;
|
|
ScopedThread thread([&] {
|
|
while (!go.load()) {
|
|
}
|
|
waitForPending<TypeParam>();
|
|
// Wait to see if `TypeParam::wait_until` wakes up from timeout.
|
|
TypeParam::sleep_for(milliseconds(1));
|
|
promise.set_value(42);
|
|
});
|
|
go = true;
|
|
auto result = TypeParam::wait_until(future, TypeParam::time_point::max());
|
|
EXPECT_THAT(result, std::future_status::ready);
|
|
}
|
|
|
|
namespace {
|
|
|
|
class ClockTypesTestNames {
|
|
public:
|
|
template <typename T>
|
|
static std::string GetName(int) {
|
|
std::string result;
|
|
result += clockName<T>();
|
|
result += "_";
|
|
result += durationName<T>();
|
|
return result;
|
|
}
|
|
|
|
private:
|
|
template <typename T>
|
|
static const char* clockName() {
|
|
using Clock = typename std::tuple_element<0, T>::type;
|
|
if constexpr (std::is_same_v<Clock, kotlin::steady_clock>) {
|
|
return "steady_clock";
|
|
} else if constexpr (std::is_same_v<Clock, kotlin::test_support::manual_clock>) {
|
|
return "manual_clock";
|
|
} else {
|
|
return "unknown";
|
|
}
|
|
}
|
|
|
|
template <typename T>
|
|
static const char* durationName() {
|
|
using Duration = typename std::tuple_element<1, T>::type;
|
|
if constexpr (std::is_same_v<Duration, std::chrono::nanoseconds>) {
|
|
return "ns";
|
|
} else if constexpr (std::is_same_v<Duration, std::chrono::microseconds>) {
|
|
return "us";
|
|
} else if constexpr (std::is_same_v<Duration, std::chrono::milliseconds>) {
|
|
return "ms";
|
|
} else if constexpr (std::is_same_v<Duration, std::chrono::seconds>) {
|
|
return "s";
|
|
} else if constexpr (std::is_same_v<Duration, std::chrono::minutes>) {
|
|
return "m";
|
|
} else if constexpr (std::is_same_v<Duration, std::chrono::hours>) {
|
|
return "h";
|
|
} else if constexpr (std::is_same_v<Duration, kotlin::nanoseconds>) {
|
|
return "sat_ns";
|
|
} else if constexpr (std::is_same_v<Duration, kotlin::microseconds>) {
|
|
return "sat_us";
|
|
} else if constexpr (std::is_same_v<Duration, kotlin::milliseconds>) {
|
|
return "sat_ms";
|
|
} else if constexpr (std::is_same_v<Duration, kotlin::seconds>) {
|
|
return "sat_s";
|
|
} else if constexpr (std::is_same_v<Duration, kotlin::minutes>) {
|
|
return "sat_m";
|
|
} else if constexpr (std::is_same_v<Duration, kotlin::hours>) {
|
|
return "sat_h";
|
|
} else {
|
|
return "unknown";
|
|
}
|
|
}
|
|
};
|
|
|
|
} // namespace
|
|
|
|
template <typename T>
|
|
class ClockTypesTest : public testing::Test {
|
|
public:
|
|
using Clock = typename std::tuple_element<0, T>::type;
|
|
using Duration = typename std::tuple_element<1, T>::type;
|
|
};
|
|
|
|
using ClockTypesTestTypes = testing::Types<
|
|
std::tuple<kotlin::steady_clock, std::chrono::nanoseconds>,
|
|
std::tuple<kotlin::steady_clock, std::chrono::microseconds>,
|
|
std::tuple<kotlin::steady_clock, std::chrono::milliseconds>,
|
|
std::tuple<kotlin::steady_clock, std::chrono::seconds>,
|
|
std::tuple<kotlin::steady_clock, std::chrono::minutes>,
|
|
std::tuple<kotlin::steady_clock, std::chrono::hours>,
|
|
std::tuple<kotlin::steady_clock, kotlin::nanoseconds>,
|
|
std::tuple<kotlin::steady_clock, kotlin::microseconds>,
|
|
std::tuple<kotlin::steady_clock, kotlin::milliseconds>,
|
|
std::tuple<kotlin::steady_clock, kotlin::seconds>,
|
|
std::tuple<kotlin::steady_clock, kotlin::minutes>,
|
|
std::tuple<kotlin::steady_clock, kotlin::hours>,
|
|
std::tuple<kotlin::test_support::manual_clock, std::chrono::nanoseconds>,
|
|
std::tuple<kotlin::test_support::manual_clock, std::chrono::microseconds>,
|
|
std::tuple<kotlin::test_support::manual_clock, std::chrono::milliseconds>,
|
|
std::tuple<kotlin::test_support::manual_clock, std::chrono::seconds>,
|
|
std::tuple<kotlin::test_support::manual_clock, std::chrono::minutes>,
|
|
std::tuple<kotlin::test_support::manual_clock, std::chrono::hours>,
|
|
std::tuple<kotlin::test_support::manual_clock, kotlin::nanoseconds>,
|
|
std::tuple<kotlin::test_support::manual_clock, kotlin::microseconds>,
|
|
std::tuple<kotlin::test_support::manual_clock, kotlin::milliseconds>,
|
|
std::tuple<kotlin::test_support::manual_clock, kotlin::seconds>,
|
|
std::tuple<kotlin::test_support::manual_clock, kotlin::minutes>,
|
|
std::tuple<kotlin::test_support::manual_clock, kotlin::hours>>;
|
|
TYPED_TEST_SUITE(ClockTypesTest, ClockTypesTestTypes, ClockTypesTestNames);
|
|
|
|
TYPED_TEST(ClockTypesTest, SleepFor) {
|
|
using Clock = typename ClockTypesTest<TypeParam>::Clock;
|
|
using Duration = typename ClockTypesTest<TypeParam>::Duration;
|
|
static_assert(std::is_same_v<void, decltype(Clock::sleep_for(std::declval<Duration>()))>);
|
|
}
|
|
|
|
TYPED_TEST(ClockTypesTest, SleepUntil) {
|
|
using Clock = typename ClockTypesTest<TypeParam>::Clock;
|
|
using Duration = typename ClockTypesTest<TypeParam>::Duration;
|
|
static_assert(std::is_same_v<void, decltype(Clock::sleep_until(std::declval<std::chrono::time_point<Clock, Duration>>()))>);
|
|
}
|
|
|
|
TYPED_TEST(ClockTypesTest, CVWaitFor) {
|
|
using Clock = typename ClockTypesTest<TypeParam>::Clock;
|
|
using Duration = typename ClockTypesTest<TypeParam>::Duration;
|
|
static_assert(std::is_same_v<
|
|
bool,
|
|
decltype(Clock::wait_for(
|
|
std::declval<std::condition_variable&>(), std::declval<std::unique_lock<std::mutex>&>(), std::declval<Duration>(),
|
|
std::declval<std::function<bool()>>()))>);
|
|
}
|
|
|
|
TYPED_TEST(ClockTypesTest, CVWaitUntil) {
|
|
using Clock = typename ClockTypesTest<TypeParam>::Clock;
|
|
using Duration = typename ClockTypesTest<TypeParam>::Duration;
|
|
static_assert(std::is_same_v<
|
|
bool,
|
|
decltype(Clock::wait_until(
|
|
std::declval<std::condition_variable&>(), std::declval<std::unique_lock<std::mutex>&>(),
|
|
std::declval<std::chrono::time_point<Clock, Duration>>(), std::declval<std::function<bool()>>()))>);
|
|
}
|
|
|
|
TYPED_TEST(ClockTypesTest, CVAnyWaitFor) {
|
|
using Clock = typename ClockTypesTest<TypeParam>::Clock;
|
|
using Duration = typename ClockTypesTest<TypeParam>::Duration;
|
|
static_assert(std::is_same_v<
|
|
bool,
|
|
decltype(Clock::wait_for(
|
|
std::declval<std::condition_variable_any&>(), std::declval<std::unique_lock<std::shared_mutex>&>(),
|
|
std::declval<Duration>(), std::declval<std::function<bool()>>()))>);
|
|
}
|
|
|
|
TYPED_TEST(ClockTypesTest, CVAnyWaitUntil) {
|
|
using Clock = typename ClockTypesTest<TypeParam>::Clock;
|
|
using Duration = typename ClockTypesTest<TypeParam>::Duration;
|
|
static_assert(std::is_same_v<
|
|
bool,
|
|
decltype(Clock::wait_until(
|
|
std::declval<std::condition_variable_any&>(), std::declval<std::unique_lock<std::shared_mutex>&>(),
|
|
std::declval<std::chrono::time_point<Clock, Duration>>(), std::declval<std::function<bool()>>()))>);
|
|
}
|
|
|
|
TYPED_TEST(ClockTypesTest, FutureWaitFor) {
|
|
using Clock = typename ClockTypesTest<TypeParam>::Clock;
|
|
using Duration = typename ClockTypesTest<TypeParam>::Duration;
|
|
static_assert(std::is_same_v<
|
|
std::future_status, decltype(Clock::wait_for(std::declval<const std::future<int>&>(), std::declval<Duration>()))>);
|
|
}
|
|
|
|
TYPED_TEST(ClockTypesTest, FutureWaitUntil) {
|
|
using Clock = typename ClockTypesTest<TypeParam>::Clock;
|
|
using Duration = typename ClockTypesTest<TypeParam>::Duration;
|
|
static_assert(std::is_same_v<
|
|
std::future_status,
|
|
decltype(Clock::wait_until(
|
|
std::declval<const std::future<int>&>(), std::declval<std::chrono::time_point<Clock, Duration>>()))>);
|
|
}
|
|
|
|
TYPED_TEST(ClockTypesTest, SharedFutureWaitFor) {
|
|
using Clock = typename ClockTypesTest<TypeParam>::Clock;
|
|
using Duration = typename ClockTypesTest<TypeParam>::Duration;
|
|
static_assert(std::is_same_v<
|
|
std::future_status, decltype(Clock::wait_for(std::declval<const std::shared_future<int>&>(), std::declval<Duration>()))>);
|
|
}
|
|
|
|
TYPED_TEST(ClockTypesTest, SharedFutureWaitUntil) {
|
|
using Clock = typename ClockTypesTest<TypeParam>::Clock;
|
|
using Duration = typename ClockTypesTest<TypeParam>::Duration;
|
|
static_assert(std::is_same_v<
|
|
std::future_status,
|
|
decltype(Clock::wait_until(
|
|
std::declval<const std::shared_future<int>&>(), std::declval<std::chrono::time_point<Clock, Duration>>()))>);
|
|
}
|
|
|
|
TEST(ManualClockTest, SleepUntil) {
|
|
test_support::manual_clock::reset();
|
|
|
|
auto before = test_support::manual_clock::now();
|
|
test_support::manual_clock::sleep_until(before + seconds(2));
|
|
EXPECT_THAT(test_support::manual_clock::now() - before, seconds(2));
|
|
// Sleep until current time.
|
|
test_support::manual_clock::sleep_until(before + seconds(2));
|
|
EXPECT_THAT(test_support::manual_clock::now() - before, seconds(2));
|
|
// Sleep until moment in the past.
|
|
test_support::manual_clock::sleep_until(before);
|
|
EXPECT_THAT(test_support::manual_clock::now() - before, seconds(2));
|
|
}
|
|
|
|
TEST(ManualClockTest, Pending) {
|
|
test_support::manual_clock::reset();
|
|
|
|
// Nothing pending at start.
|
|
EXPECT_THAT(test_support::manual_clock::pending(), std::nullopt);
|
|
std::promise<int> promise;
|
|
std::future<int> future = promise.get_future();
|
|
ScopedThread thread([&] { test_support::manual_clock::wait_for(future, seconds(1)); });
|
|
test_support::manual_clock::waitForPending(test_support::manual_clock::now() + seconds(1));
|
|
|
|
// Unblocks the thread.
|
|
test_support::manual_clock::sleep_for(seconds(1));
|
|
thread.join();
|
|
|
|
// Nothing pending anymore.
|
|
EXPECT_THAT(test_support::manual_clock::pending(), std::nullopt);
|
|
}
|
|
|
|
TEST(ManualClockTest, ConcurrentSleepUntil) {
|
|
test_support::manual_clock::reset();
|
|
|
|
constexpr auto threadCount = kDefaultThreadCount;
|
|
std::vector<ScopedThread> threads;
|
|
std::atomic<bool> run = false;
|
|
std::atomic<int> ready = 0;
|
|
for (int i = 0; i < threadCount; ++i) {
|
|
threads.emplace_back([&, i] {
|
|
auto now = test_support::manual_clock::now();
|
|
++ready;
|
|
while (!run.load()) {
|
|
}
|
|
test_support::manual_clock::sleep_until(now + seconds(i));
|
|
});
|
|
}
|
|
auto before = test_support::manual_clock::now();
|
|
while (ready.load() < threadCount) {
|
|
}
|
|
run = true;
|
|
threads.clear();
|
|
auto after = test_support::manual_clock::now();
|
|
EXPECT_THAT(after - before, seconds(threadCount - 1));
|
|
}
|
|
|
|
TEST(ManualClockTest, ConcurrentWaits) {
|
|
test_support::manual_clock::reset();
|
|
|
|
constexpr auto threadCount = kDefaultThreadCount;
|
|
std::vector<ScopedThread> threads;
|
|
std::mutex mutex;
|
|
std::condition_variable cv;
|
|
std::condition_variable_any cvAny;
|
|
std::promise<int> promise1;
|
|
std::promise<int> promise2;
|
|
std::future<int> future = promise1.get_future();
|
|
std::shared_future<int> shared_future = promise2.get_future().share();
|
|
std::atomic<bool> run = false;
|
|
std::atomic<int> ready = 0;
|
|
for (int i = 0; i < threadCount; ++i) {
|
|
threads.emplace_back([&, i] {
|
|
auto now = test_support::manual_clock::now();
|
|
++ready;
|
|
while (!run.load()) {
|
|
}
|
|
switch (i % 4) {
|
|
case 0: {
|
|
std::unique_lock guard(mutex);
|
|
test_support::manual_clock::wait_until(cv, guard, now + seconds(i / 3 + 1), [] { return false; });
|
|
}
|
|
case 1: {
|
|
std::unique_lock guard(mutex);
|
|
test_support::manual_clock::wait_until(cvAny, guard, now + seconds(i / 3 + 1), [] { return false; });
|
|
}
|
|
case 2: {
|
|
test_support::manual_clock::wait_until(future, now + seconds(i / 3 + 1));
|
|
}
|
|
case 3: {
|
|
test_support::manual_clock::wait_until(shared_future, now + seconds(i / 3 + 1));
|
|
}
|
|
}
|
|
});
|
|
}
|
|
auto before = test_support::manual_clock::now();
|
|
while (ready.load() < threadCount) {
|
|
}
|
|
run = true;
|
|
|
|
test_support::manual_clock::sleep_until(before + seconds(1));
|
|
// Now the first 3 threads will be unblocked.
|
|
threads[0].join();
|
|
threads[1].join();
|
|
threads[2].join();
|
|
// Make sure at least one other thread is waiting.
|
|
while (!test_support::manual_clock::pending()) {
|
|
}
|
|
auto pendingAfterSecond = *test_support::manual_clock::pending();
|
|
EXPECT_THAT(pendingAfterSecond, testing::Ge(before + seconds(2)));
|
|
|
|
// Unblock all the threads.
|
|
test_support::manual_clock::sleep_until(before + seconds((threadCount - 1) / 3 + 1));
|
|
threads.clear();
|
|
// All threads are gone, nothing can possibly be pending.
|
|
EXPECT_THAT(test_support::manual_clock::pending(), std::nullopt);
|
|
}
|