1 /*
2   Copyright (c) DataStax, Inc.
3 
4   Licensed under the Apache License, Version 2.0 (the "License");
5   you may not use this file except in compliance with the License.
6   You may obtain a copy of the License at
7 
8   http://www.apache.org/licenses/LICENSE-2.0
9 
10   Unless required by applicable law or agreed to in writing, software
11   distributed under the License is distributed on an "AS IS" BASIS,
12   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   See the License for the specific language governing permissions and
14   limitations under the License.
15 */
16 
17 #include <gtest/gtest.h>
18 
19 #include "atomic.hpp"
20 #include "event_loop.hpp"
21 #include "test_utils.hpp"
22 
23 using namespace datastax::internal;
24 using namespace datastax::internal::core;
25 
26 class EventLoopUnitTest : public testing::Test {
27 public:
28   class MarkTaskCompleted : public Task {
29   public:
MarkTaskCompleted(EventLoopUnitTest * event_loop_unit_test)30     MarkTaskCompleted(EventLoopUnitTest* event_loop_unit_test)
31         : event_loop_unit_test_(event_loop_unit_test) {}
run(EventLoop * event_loop)32     virtual void run(EventLoop* event_loop) { event_loop_unit_test_->mark_task_completed(); }
33 
34   private:
35     EventLoopUnitTest* event_loop_unit_test_;
36   };
37 
38   class MarkIsRunningOn : public Task {
39   public:
MarkIsRunningOn(EventLoopUnitTest * event_loop_unit_test,EventLoop * event_loop)40     MarkIsRunningOn(EventLoopUnitTest* event_loop_unit_test, EventLoop* event_loop)
41         : event_loop_unit_test_(event_loop_unit_test)
42         , event_loop_(event_loop) {}
run(EventLoop * event_loop)43     virtual void run(EventLoop* event_loop) {
44       event_loop_unit_test_->set_is_running_on(event_loop_->is_running_on());
45     }
46 
47   private:
48     EventLoopUnitTest* event_loop_unit_test_;
49     EventLoop* event_loop_;
50   };
51 
52   class StartIoTime : public Task {
53   public:
run(EventLoop * event_loop)54     virtual void run(EventLoop* event_loop) { event_loop->maybe_start_io_time(); }
55   };
56 
57   class SetIoTimeElapsed : public Task {
58   public:
SetIoTimeElapsed(EventLoopUnitTest * event_loop_unit_test)59     SetIoTimeElapsed(EventLoopUnitTest* event_loop_unit_test)
60         : event_loop_unit_test_(event_loop_unit_test) {}
run(EventLoop * event_loop)61     virtual void run(EventLoop* event_loop) {
62       event_loop_unit_test_->set_io_time_elapsed(event_loop->io_time_elapsed());
63     }
64 
65   private:
66     EventLoopUnitTest* event_loop_unit_test_;
67   };
68 
69 public:
EventLoopUnitTest()70   EventLoopUnitTest()
71       : is_task_completed_(false)
72       , is_running_on_(false)
73       , io_time_elapsed_(0) {}
74 
is_task_completed()75   bool is_task_completed() { return is_task_completed_; }
is_running_on()76   bool is_running_on() { return is_running_on_; }
io_time_elapsed()77   uint64_t io_time_elapsed() { return io_time_elapsed_; }
78 
79 protected:
mark_task_completed()80   void mark_task_completed() { is_task_completed_ = true; }
set_is_running_on(bool is_running_on)81   void set_is_running_on(bool is_running_on) { is_running_on_ = is_running_on; }
set_io_time_elapsed(uint64_t io_time_elapsed)82   void set_io_time_elapsed(uint64_t io_time_elapsed) { io_time_elapsed_ = io_time_elapsed; }
83 
84 private:
85   bool is_task_completed_;
86   bool is_running_on_;
87   uint64_t io_time_elapsed_;
88 };
89 
90 class TestEventLoop : public EventLoop {
91 public:
TestEventLoop()92   TestEventLoop()
93       : is_on_run_completed_(false)
94       , is_after_run_completed_(false) {}
95 
is_on_run_completed()96   bool is_on_run_completed() { return is_on_run_completed_.load(); }
is_after_run_completed()97   bool is_after_run_completed() { return is_after_run_completed_; }
98 
99 protected:
on_run()100   void on_run() { is_on_run_completed_.store(true); }
on_after_run()101   void on_after_run() { is_after_run_completed_ = true; }
102 
103 private:
104   Atomic<bool> is_on_run_completed_;
105   bool is_after_run_completed_;
106 };
107 
TEST_F(EventLoopUnitTest,ExecuteTask)108 TEST_F(EventLoopUnitTest, ExecuteTask) {
109   EventLoop event_loop;
110   ASSERT_EQ(0, event_loop.init("EventLoopUnitTest::ExecuteTask"));
111   ASSERT_STREQ("EventLoopUnitTest::ExecuteTask", event_loop.name().c_str());
112   ASSERT_EQ(0, event_loop.run());
113 
114   ASSERT_FALSE(is_task_completed());
115   event_loop.add(new MarkTaskCompleted(this));
116 
117   event_loop.close_handles();
118   event_loop.join();
119   ASSERT_TRUE(is_task_completed());
120 }
121 
TEST_F(EventLoopUnitTest,ThreadRunningOn)122 TEST_F(EventLoopUnitTest, ThreadRunningOn) {
123   EventLoop event_loop;
124   ASSERT_EQ(0, event_loop.init("EventLoopUnitTest::ThreadRunningOn"));
125   ASSERT_STREQ("EventLoopUnitTest::ThreadRunningOn", event_loop.name().c_str());
126   ASSERT_EQ(0, event_loop.run());
127 
128   ASSERT_FALSE(is_running_on());
129   event_loop.add(new MarkIsRunningOn(this, &event_loop));
130 
131   event_loop.close_handles();
132   event_loop.join();
133   ASSERT_TRUE(is_running_on());
134 }
135 
TEST_F(EventLoopUnitTest,ThreadNotRunningOn)136 TEST_F(EventLoopUnitTest, ThreadNotRunningOn) {
137   EventLoop event_loop;
138   ASSERT_EQ(0, event_loop.init("EventLoopUnitTest::ThreadNotRunningOn (EventLoop 1)"));
139   ASSERT_STREQ("EventLoopUnitTest::ThreadNotRunningOn (EventLoop 1)", event_loop.name().c_str());
140   ASSERT_EQ(0, event_loop.run());
141 
142   ASSERT_FALSE(is_running_on());
143 
144   EventLoop event_loop_2;
145   ASSERT_EQ(0, event_loop_2.init("EventLoopUnitTest::ThreadNotRunningOn (EventLoop 2)"));
146   ASSERT_STREQ("EventLoopUnitTest::ThreadNotRunningOn (EventLoop 2)", event_loop_2.name().c_str());
147   ASSERT_EQ(0, event_loop_2.run());
148   event_loop_2.add(new MarkIsRunningOn(this, &event_loop));
149   event_loop_2.close_handles();
150   event_loop_2.join();
151 
152   event_loop.close_handles();
153   event_loop.join();
154   ASSERT_FALSE(is_running_on());
155 }
156 
TEST_F(EventLoopUnitTest,BeforeAndAfterRun)157 TEST_F(EventLoopUnitTest, BeforeAndAfterRun) {
158   TestEventLoop event_loop;
159 
160   ASSERT_FALSE(event_loop.is_on_run_completed());
161   ASSERT_FALSE(event_loop.is_after_run_completed());
162   ASSERT_EQ(0, event_loop.init("EventLoopUnitTest::BeforeAndAfterRun"));
163   ASSERT_STREQ("EventLoopUnitTest::BeforeAndAfterRun", event_loop.name().c_str());
164   ASSERT_EQ(0, event_loop.run());
165   while (!event_loop.is_on_run_completed())
166     test::Utils::msleep(1); // Poll to wait for thread to be started
167   ASSERT_TRUE(event_loop.is_on_run_completed());
168   ASSERT_FALSE(event_loop.is_after_run_completed());
169 
170   event_loop.close_handles();
171   event_loop.join();
172   ASSERT_TRUE(event_loop.is_on_run_completed());
173   ASSERT_TRUE(event_loop.is_after_run_completed());
174 }
175 
TEST_F(EventLoopUnitTest,IoTimeElapsed)176 TEST_F(EventLoopUnitTest, IoTimeElapsed) {
177   // TODO:
178 
179   /*
180    * io_time_elapsed() measures the time between the start of I/O processing
181    * (which is started via maybe_start_io_time()) and the end of I/O
182    * processing which is handled by a uv_check_t. A potential way to verify it
183    * would involve putting sleep inside of an I/O callback (or another
184    * uv_check_t as long as the call ordering is correct) then checking
185    * io_time_elapsed() using a uv_prepare_t on the same uv_run() iteration.
186    */
187 }
188