#include <http/response_json.h>
#include <http/tests/response_test.h>
#include <util/threads/thread.h>
+#include <util/threads/sync.h>
#include <gtest/gtest.h>
#include <sstream>
namespace {
+/// @brief Type definition for the pointer to Thread objects.
+typedef boost::shared_ptr<Thread> ThreadPtr;
+
+/// @brief Base class for tests requiring threads.
+///
+/// This class contains 3 flags to signal when the thread has
+/// started, is stopping and when it is stopped. The flags
+/// are accessed in thread safe manner.
+class ThreadedTest : public ::testing::Test {
+protected:
+
+ /// @brief Constructor.
+ ThreadedTest()
+ : thread_(), condvar_(), ready_(false), stopping_(false),
+ stopped_(false) {
+ }
+
+ /// @brief Sets selected flag to true and signals condition
+ /// variable.
+ ///
+ /// @param flag Reference to flag which should be set to true.
+ void doSignal(bool& flag) {
+ {
+ Mutex::Locker lock(mutex_);
+ flag = true;
+ }
+ condvar_.signal();
+ }
+
+ /// @brief Signal that thread is ready.
+ void signalReady() {
+ doSignal(ready_);
+ }
+
+ /// @brief Signal that thread is stopping.
+ void signalStopping() {
+ doSignal(stopping_);
+ }
+
+ /// @brief Signal that thread is stopped.
+ void signalStopped() {
+ doSignal(stopped_);
+ }
+
+ /// @brief Wait for a selected flag to be set.
+ ///
+ /// @param flag Reference to a flag on which the thread is
+ /// waiting.
+ void doWait(bool& flag) {
+ Mutex::Locker lock(mutex_);
+ while (!flag) {
+ condvar_.wait(mutex_);
+ }
+ }
+
+ /// @brief Wait for the thread to be ready.
+ void waitReady() {
+ doWait(ready_);
+ }
+
+ /// @brief Wait for the thread to be stopping.
+ void waitStopping() {
+ doWait(stopping_);
+ }
+
+ /// @brief Wait for the thread to stop.
+ void waitStopped() {
+ doWait(stopped_);
+ }
+
+ /// @brief Checks if the thread is stopping.
+ ///
+ /// @return true if the thread is stopping, false otherwise.
+ bool isStopping() {
+ Mutex::Locker lock(mutex_);
+ return (stopping_);
+ }
+
+ /// @brief Pointer to server thread.
+ ThreadPtr thread_;
+
+ /// @brief Mutex used to synchronize threads.
+ Mutex mutex_;
+
+ /// Condtional variable for thread waits.
+ CondVar condvar_;
+
+ /// Flag indicating that the thread is ready.
+ bool ready_;
+
+ /// Flag indicating that the thread is stopping.
+ bool stopping_;
+
+ /// Flag indicating that the thread is stopped.
+ bool stopped_;
+};
+
//////////////////////////////// STDOUT ////////////////////////////////
/// @brief Test derived StdoutControlSocket class.
/// @brief Test timeout in ms.
const long TEST_TIMEOUT = 10000;
-/// @brief Type definition for the pointer to Thread objects.
-typedef boost::shared_ptr<Thread> ThreadPtr;
-
/// @brief Test fixture class for unix control sockets.
-class UnixControlSocketTest : public ::testing::Test {
+class UnixControlSocketTest : public ThreadedTest {
public:
/// @brief Constructor.
- UnixControlSocketTest() : io_service_(), thread_(), ready_(false) {
+ UnixControlSocketTest()
+ : ThreadedTest(), io_service_() {
removeUnixSocketFile();
}
/// @brief Destructor.
virtual ~UnixControlSocketTest() {
- io_service_.stop();
if (thread_) {
thread_->wait();
thread_.reset();
}
+ // io_service must be stopped after the thread returns,
+ // otherwise the thread may never return if it is
+ // waiting for the completion of some asynchronous tasks.
+ io_service_.stop();
removeUnixSocketFile();
}
/// @brief Thread reflecting server function.
void reflectServer();
- /// @brief Thread timeout server function.
- void timeoutServer();
-
/// @brief IOService object.
IOService io_service_;
-
- /// @brief Pointer to server thread.
- ThreadPtr thread_;
-
- /// @brief Ready flag.
- bool ready_;
};
/// @brief Server method running in a thread reflecting the command.
socket(io_service_.get_io_service());
// Ready.
- ready_ = true;
+ signalReady();
// Timeout.
IntervalTimer timer(io_service_);
}
}
-/// @brief Server method running in a thread waiting forever.
-///
-/// It waits that ready becomes true.
-void
-UnixControlSocketTest::timeoutServer() {
- while (!ready_) {
- usleep(1000);
- }
-}
-
// Verifies that the createControlSocket template can create an unix
// control socket.
TEST_F(UnixControlSocketTest, createControlSocket) {
// Run a reflecting server in a thread.
thread_.reset(new Thread([this]() { reflectServer(); }));
- while (!ready_) {
- usleep(1000);
- }
+
+ waitReady();
// Try configGet.
ConstElementPtr reflected;
// Run a reflecting server in a thread.
thread_.reset(new Thread([this]() { reflectServer(); }));
- while (!ready_) {
- usleep(1000);
- }
+
+ waitReady();
// Prepare a config to test.
ConstElementPtr json = Element::fromJSON("{ \"bar\": 1 }");
// Run a reflecting server in a thread.
thread_.reset(new Thread([this]() { reflectServer(); }));
- while (!ready_) {
- usleep(1000);
- }
+
+ waitReady();
// Prepare a config to set.
ConstElementPtr json = Element::fromJSON("{ \"bar\": 1 }");
ASSERT_TRUE(ucs);
// Run a timeout server in a thread.
- thread_.reset(new Thread([this]() { timeoutServer(); }));
+ thread_.reset(new Thread([this]() { waitReady(); }));
// Try configGet: it should get a communication error,
EXPECT_THROW(ucs->configGet("foo"), ControlSocketError);
- ready_ = true;
+ signalReady();
}
//////////////////////////////// HTTP ////////////////////////////////
};
/// @brief Test fixture class for http control sockets.
-class HttpControlSocketTest : public ::testing::Test {
+class HttpControlSocketTest : public ThreadedTest {
public:
HttpControlSocketTest()
- : io_service_(), thread_(),
- ready_(false), done_(false), finished_(false) {
+ : ThreadedTest(), io_service_() {
}
/// @brief Destructor.
virtual ~HttpControlSocketTest() {
- io_service_.stop();
if (thread_) {
thread_->wait();
thread_.reset();
}
+ // io_service must be stopped after the thread returns,
+ // otherwise the thread may never return if it is
+ // waiting for the completion of some asynchronous tasks.
+ io_service_.stop();
}
/// @brief Returns socket URL.
/// Run IO in a thread.
void start() {
thread_.reset(new Thread([this]() {
- ready_ = true;
- while (!done_) {
- io_service_.run_one();
- }
- finished_ = true;
- io_service_.poll();
- }));
- while (!ready_) {
- usleep(1000);
- }
+ signalReady();
+ while (!isStopping()) {
+ io_service_.run_one();
+ }
+ signalStopped();
+ io_service_.poll();
+ }));
+ waitReady();
+
if (listener_) {
ASSERT_NO_THROW(listener_->start());
}
///
/// Post an empty action to finish current run_one.
void stop() {
- done_ = true;
+ signalStopping();
io_service_.post([]() { return; });
- while (!finished_) {
- usleep(1000);
- }
+ waitStopped();
+
if (listener_) {
ASSERT_NO_THROW(listener_->stop());
}
/// @brief Pointer to listener.
HttpListenerPtr listener_;
- /// @brief Pointer to server thread.
- ThreadPtr thread_;
-
- /// @brief Ready flag.
- bool ready_;
-
/// @brief Done flag (stopping thread).
bool done_;