diff --git a/include/redox/command.hpp b/include/redox/command.hpp index 9c05033..522c46b 100644 --- a/include/redox/command.hpp +++ b/include/redox/command.hpp @@ -61,6 +61,8 @@ public: */ int status() const { return reply_status_; } + std::string lastError() const { return last_error_; } + /** * Returns true if this command got a successful reply. */ @@ -138,6 +140,7 @@ private: // Place to store the reply value and status. ReplyT reply_val_; std::atomic_int reply_status_; + std::string last_error_; // How many messages sent to server but not received reply std::atomic_int pending_ = {0}; diff --git a/src/command.cpp b/src/command.cpp index 43f7bbd..45803e4 100644 --- a/src/command.cpp +++ b/src/command.cpp @@ -37,7 +37,7 @@ Command::Command( const std::function&)>& callback, double repeat, double after, bool free_memory, log::Logger& logger ) : rdx_(rdx), id_(id), cmd_(cmd), repeat_(repeat), after_(after), free_memory_(free_memory), - callback_(callback), logger_(logger) { + callback_(callback), last_error_(), logger_(logger) { timer_guard_.lock(); } @@ -51,11 +51,13 @@ void Command::wait() { template void Command::processReply(redisReply* r) { + last_error_.clear(); reply_obj_ = r; if(reply_obj_ == nullptr) { reply_status_ = ERROR_REPLY; - logger_.error() << "Received null redisReply* from hiredis."; + last_error_ = "Received null redisReply* from hiredis."; + logger_.error() << last_error_; Redox::disconnectedCallback(rdx_->ctx_, REDIS_ERR); } else { @@ -121,7 +123,7 @@ ReplyT Command::reply() { template std::string Command::cmd() const { return rdx_->vecToStr(cmd_); -}; +} template bool Command::isExpectedReply(int type) { @@ -133,8 +135,11 @@ bool Command::isExpectedReply(int type) { if(checkErrorReply() || checkNilReply()) return false; - logger_.error() << cmd() << ": Received reply of type " << reply_obj_->type - << ", expected type " << type << "."; + std::stringstream errorMessage; + errorMessage << "Received reply of type " << reply_obj_->type + << ", expected type " << type << "."; + last_error_ = errorMessage.str(); + logger_.error() << cmd() << ": " << last_error_; reply_status_ = WRONG_TYPE; return false; } @@ -149,8 +154,11 @@ bool Command::isExpectedReply(int typeA, int typeB) { if(checkErrorReply() || checkNilReply()) return false; - logger_.error() << cmd() << ": Received reply of type " << reply_obj_->type - << ", expected type " << typeA << " or " << typeB << "."; + std::stringstream errorMessage; + errorMessage << "Received reply of type " << reply_obj_->type + << ", expected type " << typeA << " or " << typeB << "."; + last_error_ = errorMessage.str(); + logger_.error() << cmd() << ": " << last_error_; reply_status_ = WRONG_TYPE; return false; } @@ -159,7 +167,8 @@ template bool Command::checkErrorReply() { if (reply_obj_->type == REDIS_REPLY_ERROR) { - logger_.error() << cmd() << ": " << reply_obj_->str; + last_error_ = reply_obj_->str; + logger_.error() << cmd() << ": " << last_error_; reply_status_ = ERROR_REPLY; return true; } diff --git a/test/test.cpp b/test/test.cpp index 60981cd..56612b2 100644 --- a/test/test.cpp +++ b/test/test.cpp @@ -67,7 +67,7 @@ protected: cmd_count++; return [this, value](Command& c) { EXPECT_TRUE(c.ok()); - if(c.ok()) EXPECT_EQ(c.reply(), value); + if(c.ok()) EXPECT_EQ(value, c.reply()); cmd_count--; cmd_waiter.notify_all(); }; @@ -93,6 +93,22 @@ protected: } /** + * Check the error + */ + template + Callback print_and_check_error(const ReplyT& value) { + cmd_count++; + return [this, value](Command& c) { + EXPECT_FALSE(c.ok()); + EXPECT_FALSE(c.lastError().empty()); +// EXPECT_EQ(value, c.reply()); + cout << c.cmd() << ": " << c.lastError() << endl; + cmd_count--; + cmd_waiter.notify_all(); + }; + } + + /** * Wait until all async commands that used check() as a callback * complete. */ @@ -100,7 +116,7 @@ protected: unique_lock ul(cmd_waiter_lock); cmd_waiter.wait(ul, [this] { return (cmd_count == 0); }); rdx.disconnect(); - }; + } template void check_sync(Command& c, const ReplyT& value) { @@ -116,6 +132,17 @@ protected: cout << "[SYNC] " << c.cmd() << ": " << c.reply() << endl; c.free(); } + + /** + * Check the error + */ + template + void print_and_check_error_sync(Command& c, const ReplyT& value) { + EXPECT_FALSE(c.ok()); + EXPECT_FALSE(c.lastError().empty()); +// EXPECT_EQ(value, c.reply()); + cout << c.cmd() << ": " << c.lastError() << endl; + } }; // ------------------------------------------- @@ -170,6 +197,12 @@ TEST_F(RedoxTest, Loop) { wait_for_replies(); } +TEST_F(RedoxTest, GetSetError) { + rdx.command({"SET", "redox_test:a", "apple"}, print_and_check("OK")); + rdx.command({"GET", "redox_test:a"}, print_and_check_error(3)); + wait_for_replies(); +} + // ------------------------------------------- // Core unit tests - synchronous // ------------------------------------------- @@ -196,6 +229,12 @@ TEST_F(RedoxTest, IncrSync) { rdx.disconnect(); } +TEST_F(RedoxTest, GetSetSyncError) { + print_and_check_sync(rdx.commandSync({"SET", "redox_test:a", "apple"}), "OK"); + print_and_check_error_sync(rdx.commandSync({"GET", "redox_test:a"}), 3); + rdx.disconnect(); +} + // ------------------------------------------- // End tests // -------------------------------------------