/** * Redox - A modern, asynchronous, and wicked fast C++11 client for Redis * * https://github.com/hmartiro/redox * * Copyright 2015 - Hayk Martirosyan * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "redox.hpp" #include "gtest/gtest.h" namespace { using namespace std; using redox::Redox; using redox::Command; // ------------------------------------------ // The fixture for testing class Redox. // ------------------------------------------ class RedoxTest : public ::testing::Test { protected: Redox rdx = {"localhost", 6379}; RedoxTest() { // Connect to the server rdx.connect(); // Clear all keys used by the tests here rdx.command("DEL redox_test:a"); } virtual ~RedoxTest() { } // CV and counter to wait for async commands to complete atomic_int cmd_count = {0}; condition_variable cmd_waiter; mutex cmd_waiter_lock; // To make the callback code nicer template using Callback = std::function&)>; /** * Helper function that returns a command callback to print out the * command/reply and to test the reply against the provided value. */ template Callback check(const ReplyT& value) { cmd_count++; return [this, value](Command& c) { EXPECT_TRUE(c.ok()); if(c.ok()) EXPECT_EQ(c.reply(), value); cmd_count--; cmd_waiter.notify_all(); }; } /** * Wrapper for the callback that also prints out the command. */ template Callback print(Callback callback) { return [callback](Command& c) { if(c.ok()) cout << "[ASYNC] " << c.cmd() << ": " << c.reply() << endl; callback(c); }; } /** * Combination of print and check for simplicity. */ template Callback print_and_check(const ReplyT& value) { return print(check(value)); } /** * Wait until all async commands that used check() as a callback * complete. */ void wait_for_replies() { 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) { ASSERT_TRUE(c.ok()); EXPECT_EQ(c.reply(), value); c.free(); } template void print_and_check_sync(Command& c, const ReplyT& value) { ASSERT_TRUE(c.ok()); EXPECT_EQ(c.reply(), value); cout << "[SYNC] " << c.cmd_ << ": " << c.reply() << endl; c.free(); } }; // ------------------------------------------- // Core unit tests - asynchronous // ------------------------------------------- TEST_F(RedoxTest, GetSet) { rdx.command("SET redox_test:a apple", print_and_check("OK")); rdx.command("GET redox_test:a", print_and_check("apple")); wait_for_replies(); } TEST_F(RedoxTest, Delete) { rdx.command("SET redox_test:a apple", print_and_check("OK")); rdx.command("DEL redox_test:a", print_and_check(1)); rdx.command("GET redox_test:a", check(nullptr)); wait_for_replies(); } TEST_F(RedoxTest, Incr) { int count = 100; for(int i = 0; i < count; i++) { rdx.command("INCR redox_test:a", check(i+1)); } rdx.command("GET redox_test:a", print_and_check(to_string(count))); wait_for_replies(); } TEST_F(RedoxTest, Delayed) { rdx.commandDelayed("INCR redox_test:a", check(1), 0.1); this_thread::sleep_for(chrono::milliseconds(150)); rdx.command("GET redox_test:a", print_and_check(to_string(1))); wait_for_replies(); } TEST_F(RedoxTest, Loop) { int count = 0; int target_count = 20; double dt = 0.005; Command& cmd = rdx.commandLoop("INCR redox_test:a", [this, &count](Command& c) { check(++count)(c); }, dt ); double wait_time = dt * (target_count - 0.5); this_thread::sleep_for(std::chrono::duration(wait_time)); cmd.free(); rdx.command("GET redox_test:a", print_and_check(to_string(target_count))); wait_for_replies(); } // ------------------------------------------- // Core unit tests - synchronous // ------------------------------------------- TEST_F(RedoxTest, GetSetSync) { print_and_check_sync(rdx.commandSync("SET redox_test:a apple"), "OK"); print_and_check_sync(rdx.commandSync("GET redox_test:a"), "apple"); rdx.disconnect(); } TEST_F(RedoxTest, DeleteSync) { print_and_check_sync(rdx.commandSync("SET redox_test:a apple"), "OK"); print_and_check_sync(rdx.commandSync("DEL redox_test:a"), 1); check_sync(rdx.commandSync("GET redox_test:a"), nullptr); rdx.disconnect(); } TEST_F(RedoxTest, IncrSync) { int count = 100; for(int i = 0; i < count; i++) { check_sync(rdx.commandSync("INCR redox_test:a"), i+1); } print_and_check_sync(rdx.commandSync("GET redox_test:a"), to_string(count)); rdx.disconnect(); } // ------------------------------------------- // End tests // ------------------------------------------- } // namespace int main(int argc, char **argv) { ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); }