1616#include " google/cloud/testing_util/chrono_output.h"
1717#include " google/cloud/testing_util/status_matchers.h"
1818#include < gmock/gmock.h>
19+ #include < sstream>
1920#include < thread>
2021
2122namespace google {
@@ -26,7 +27,9 @@ namespace {
2627
2728using ::google::cloud::testing_util::IsOk;
2829using ::google::cloud::testing_util::StatusIs;
29- using testing::Each;
30+ using ::testing::Each;
31+ using ::testing::ElementsAreArray;
32+ using ::testing::WhenSorted;
3033
3134TEST (TimerQueueTest, ScheduleSingleRunner) {
3235 auto tq = TimerQueue::Create ();
@@ -76,27 +79,62 @@ TEST(TimerQueueTest, CancelTimers) {
7679 for (auto & t : runners) t.join ();
7780}
7881
79- TEST (TimerQueueTest, ScheduleEarlierTimerSingleRunner) {
82+ // / @test Verify timers are executed in order.
83+ TEST (TimerQueueTest, SingleRunnerOrdering) {
84+ // Populate some delays with intentionally out-of-order and in-order data.
85+ using duration = std::chrono::system_clock::duration;
86+ std::vector<duration> const delays{
87+ duration (10 ), duration (9 ), duration (8 ), duration (5 ), duration (20 ),
88+ duration (21 ), duration (22 ), duration (4 ), duration (3 ),
89+ };
90+
91+ // Create the timer queue, but do not run the servicing thread until the
92+ // timers are scheduled. Otherwise "later" timers may be immediately expired
93+ // because clocks are hard.
8094 auto tq = TimerQueue::Create ();
95+
96+ auto const start = std::chrono::system_clock::now ();
97+ std::vector<std::chrono::system_clock::time_point> expected (delays.size ());
98+ std::transform (delays.begin (), delays.end (), expected.begin (),
99+ [start](auto d) { return start + d; });
100+ // We are going to store the expiration of each timer in this vector. Note
101+ // that first it is used only by the timer servicing thread (`std::thread t`)
102+ // below, and then by the testing thread. Synchronization is provided by
103+ // the thread join() call.
104+ std::vector<std::chrono::system_clock::time_point> actual;
105+ std::vector<future<Status>> timers (expected.size ());
106+ std::transform (expected.begin (), expected.end (), timers.begin (), [&](auto d) {
107+ // As the timers expire we add their time (if successful) to `expirations`,
108+ // and return the `Status`.
109+ return tq->Schedule (d).then ([&, d](auto f) {
110+ auto e = f.get ();
111+ if (!e) return std::move (e).status ();
112+ if (*e != d) {
113+ std::ostringstream os;
114+ os << " mismatched expiration, expected=" << d << " , got=" << *e;
115+ return Status (StatusCode::kFailedPrecondition , std::move (os).str ());
116+ }
117+ actual.push_back (*std::move (e));
118+ return Status{};
119+ });
120+ });
121+
122+ // Start expiring timers.
81123 std::thread t ([tq] { tq->Service (); });
82- auto const duration = std::chrono::milliseconds (50 );
83- auto const now = std::chrono::system_clock::now ();
84- auto later = tq->Schedule (now + 2 * duration);
85- auto earlier = tq->Schedule (now + duration);
86- auto earlier_expire_time = earlier
87- .then ([&](auto f) {
88- EXPECT_FALSE (later.is_ready ());
89- return f.get ();
90- })
91- .get ();
92- auto later_expire_time = later.get ();
124+
125+ // Wait for all the timers to expire.
126+ std::vector<Status> status (timers.size ());
127+ std::transform (timers.begin (), timers.end (), status.begin (),
128+ [](auto & f) { return f.get (); });
129+
93130 tq->Shutdown ();
94131 t.join ();
95132
96- ASSERT_THAT (earlier_expire_time, IsOk ());
97- ASSERT_THAT (later_expire_time, IsOk ());
98- EXPECT_EQ (*earlier_expire_time, now + duration);
99- EXPECT_EQ (*later_expire_time, now + 2 * duration);
133+ EXPECT_THAT (status, Each (IsOk ()));
134+
135+ // At this point we expect the expirations to be in order and match the
136+ // expiration times set in `expected`.
137+ EXPECT_THAT (expected, WhenSorted (ElementsAreArray (actual)));
100138}
101139
102140TEST (TimerQueueTest, ScheduleMultipleRunners) {
@@ -184,33 +222,6 @@ TEST(TimerQueueTest, ShutdownMultipleRunners) {
184222 EXPECT_THAT (status, Each (StatusIs (StatusCode::kCancelled )));
185223}
186224
187- TEST (TimerQueueTest, ScheduleEarlierTimerMultipleRunner) {
188- auto tq = TimerQueue::Create ();
189- auto constexpr kRunners = 8 ;
190- std::vector<std::thread> runners;
191- for (auto i = 0 ; i != kRunners ; ++i) {
192- runners.emplace_back ([&] { tq->Service (); });
193- }
194- auto const duration = std::chrono::milliseconds (50 );
195- auto now = std::chrono::system_clock::now ();
196- auto later = tq->Schedule (now + 2 * duration);
197- auto earlier = tq->Schedule (now + duration);
198- auto earlier_expire_time = earlier
199- .then ([&](auto f) {
200- EXPECT_FALSE (later.is_ready ());
201- return f.get ();
202- })
203- .get ();
204- auto later_expire_time = later.get ();
205- tq->Shutdown ();
206- for (auto & t : runners) t.join ();
207-
208- ASSERT_THAT (earlier_expire_time, IsOk ());
209- ASSERT_THAT (later_expire_time, IsOk ());
210- EXPECT_EQ (*earlier_expire_time, now + duration);
211- EXPECT_EQ (*later_expire_time, now + 2 * duration);
212- }
213-
214225} // namespace
215226} // namespace internal
216227GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_END
0 commit comments