diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 000000000..c65b6b312 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,6 @@ +[submodule "deps/googletest"] + path = deps/googletest + url = https://github.com/google/googletest.git +[submodule "deps/googlemock"] + path = deps/googlemock + url = https://github.com/google/googlemock.git diff --git a/.ycm_extra_conf.py b/.ycm_extra_conf.py index 2fca4f841..9b241abb0 100644 --- a/.ycm_extra_conf.py +++ b/.ycm_extra_conf.py @@ -30,6 +30,10 @@ '/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/c++/v1', '-I', os.environ['BOOST_ROOT'], + # add dependency to googletest and googlemock + '-I', 'deps/googletest/googletest/include', + '-I', 'deps/googletest/googlemock/include', + # Always enable debugging for the project when building for semantic # completion. '-DBOOST_NETWORK_DEBUG', diff --git a/CMakeLists.txt b/CMakeLists.txt index ec04a3a57..52b995476 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -95,6 +95,7 @@ if (Boost_FOUND) enable_testing() add_subdirectory(libs/network/src) if (CPP-NETLIB_BUILD_TESTS) + add_subdirectory(deps/googletest) add_subdirectory(libs/network/test) endif (CPP-NETLIB_BUILD_TESTS) if (CPP-NETLIB_BUILD_EXPERIMENTS) diff --git a/boost/network/message.hpp b/boost/network/message.hpp index 9b4f08bf8..e262e91fc 100644 --- a/boost/network/message.hpp +++ b/boost/network/message.hpp @@ -101,6 +101,18 @@ struct basic_message { friend struct detail::directive_base; friend struct detail::wrapper_base >; + // Define an equality operator that's only available via ADL. + friend bool operator==(basic_message const& l, basic_message const& r) { + return l.headers_ == r.headers_ && l.source_ == r.source_ && + l.destination_ == r.destination_ && l.body_ == r.body_; + } + + // Define an inequality operator that's only available via ADL in terms of + // equality defined above. + friend bool operator!=(basic_message const& l, basic_message const& r) { + return !(l == r); + } + mutable headers_container_type headers_; mutable string_type body_; mutable string_type source_; diff --git a/deps/googlemock b/deps/googlemock new file mode 160000 index 000000000..f7d03d273 --- /dev/null +++ b/deps/googlemock @@ -0,0 +1 @@ +Subproject commit f7d03d2734759ee12b57d2dbcb695607d89e8e05 diff --git a/deps/googletest b/deps/googletest new file mode 160000 index 000000000..ddb8012eb --- /dev/null +++ b/deps/googletest @@ -0,0 +1 @@ +Subproject commit ddb8012eb48bc203aa93dcc2b22c1db516302b29 diff --git a/libs/network/test/CMakeLists.txt b/libs/network/test/CMakeLists.txt index 7d1e5386e..a8be0cfdf 100644 --- a/libs/network/test/CMakeLists.txt +++ b/libs/network/test/CMakeLists.txt @@ -1,9 +1,10 @@ -# Copyright (c) Dean Michael Berris 2010. +# Copyright (c) Dean Michael Berris 2010. +# Copyright 2015 Google, Inc. # Distributed under the Boost Software License, Version 1.0. -# (See accompanying file LICENSE_1_0.txt or copy at -# http://www.boost.org/LICENSE_1_0.txt) +# (See accompanying file LICENSE_1_0.txt or copy at +# http://www.boost.org/LICENSE_1_0.txt) -include_directories(${CPP-NETLIB_SOURCE_DIR}) +include_directories(${CPP-NETLIB_SOURCE_DIR} ${gtest_SOURCE_DIR}/include) add_subdirectory(uri) add_subdirectory(http) @@ -17,26 +18,28 @@ if (Boost_FOUND) # utils_base64_test -- turn on when ready. ) foreach (test ${TESTS}) - if (${CMAKE_CXX_COMPILER_ID} MATCHES GNU) - set_source_files_properties(${test}.cpp - PROPERTIES COMPILE_FLAGS "-Wall") - endif() - add_executable(cpp-netlib-${test} ${test}.cpp) - add_dependencies(cpp-netlib-${test} cppnetlib-uri) - target_link_libraries(cpp-netlib-${test} ${Boost_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} cppnetlib-uri) - if (OPENSSL_FOUND) - target_link_libraries(cpp-netlib-${test} ${OPENSSL_LIBRARIES}) - endif() - if (${CMAKE_CXX_COMPILER_ID} MATCHES GNU AND ${CMAKE_SYSTEM_NAME} MATCHES "Windows") - target_link_libraries(cpp-netlib-${test} ws2_32 wsock32) - endif() - if (${CMAKE_SYSTEM_NAME} MATCHES "Linux") - target_link_libraries(cpp-netlib-${test} rt) - endif() - set_target_properties(cpp-netlib-${test} - PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CPP-NETLIB_BINARY_DIR}/tests) - add_test(cpp-netlib-${test} - ${CPP-NETLIB_BINARY_DIR}/tests/cpp-netlib-${test}) + if (${CMAKE_CXX_COMPILER_ID} MATCHES GNU) + set_source_files_properties(${test}.cpp + PROPERTIES COMPILE_FLAGS "-Wall") + endif() + add_executable(cpp-netlib-${test} ${test}.cpp) + add_dependencies(cpp-netlib-${test} cppnetlib-uri) + target_link_libraries(cpp-netlib-${test} + ${Boost_THREAD_LIBRARY} ${Boost_SYSTEM_LIBRARY} + ${CMAKE_THREAD_LIBS_INIT} cppnetlib-uri gtest_main) + if (OPENSSL_FOUND) + target_link_libraries(cpp-netlib-${test} ${OPENSSL_LIBRARIES}) + endif() + if (${CMAKE_CXX_COMPILER_ID} MATCHES GNU AND ${CMAKE_SYSTEM_NAME} MATCHES "Windows") + target_link_libraries(cpp-netlib-${test} ws2_32 wsock32) + endif() + if (${CMAKE_SYSTEM_NAME} MATCHES "Linux") + target_link_libraries(cpp-netlib-${test} rt) + endif() + set_target_properties(cpp-netlib-${test} + PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CPP-NETLIB_BINARY_DIR}/tests) + add_test(cpp-netlib-${test} + ${CPP-NETLIB_BINARY_DIR}/tests/cpp-netlib-${test}) endforeach (test) # Also copy the server directory to the root of the build directory. diff --git a/libs/network/test/message_test.cpp b/libs/network/test/message_test.cpp index d31429682..90ce480a8 100644 --- a/libs/network/test/message_test.cpp +++ b/libs/network/test/message_test.cpp @@ -1,177 +1,65 @@ -// Copyright Dean Michael Berris 2007. +// Copyright Dean Michael Berris 2007. +// Copyright 2015 Google, Inc. // Distributed under the Boost Software License, Version 1.0. -// (See accompanying file LICENSE_1_0.txt or copy at -// http://www.boost.org/LICENSE_1_0.txt) +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) -#define BOOST_TEST_MODULE message test -#include -#include -#include +// Migrated from using Boost.Test to using googletest instead. +#include #include -#include +#include +#include -using namespace boost::network; +namespace { -typedef boost::mpl::list tag_types; +namespace http = boost::network::http; +namespace network = boost::network; -struct string_header_name { - static std::string string; +template +class MessageTest : public ::testing::Test { }; -std::string string_header_name::string = "Header"; - -struct wstring_header_name { - static std::wstring string; -}; - -std::wstring wstring_header_name::string = L"Header"; - -struct string_header_value { - static std::string string; -}; - -std::string string_header_value::string = "Value"; - -struct wstring_header_value { - static std::wstring string; -}; - -std::wstring wstring_header_value::string = L"Value"; - -template -struct header_name : string_header_name {}; - -template <> -struct header_name : wstring_header_name {}; - -template -struct header_value : string_header_value {}; - -template <> -struct header_value : wstring_header_value {}; - -struct string_body_data { - static std::string string; -}; - -std::string string_body_data::string = - "The quick brown fox jumps over the lazy dog."; - -struct wstring_body_data { - static std::wstring string; -}; - -std::wstring wstring_body_data::string = - L"The quick brown fox jumps over the lazy dog."; - -template -struct body_data : string_body_data {}; - -template <> -struct body_data : wstring_body_data {}; - -struct string_source_data { - static std::string string; -}; - -std::string string_source_data::string = "Source"; - -struct wstring_source_data { - static std::wstring string; -}; - -std::wstring wstring_source_data::string = L"Source"; - -template -struct source_data : string_source_data {}; - -template <> -struct source_data : wstring_body_data {}; - -struct string_destination_data { - static std::string string; -}; - -std::string string_destination_data::string = "Destination"; - -struct wstring_destination_data { - static std::wstring string; -}; - -std::wstring wstring_destination_data::string = L"Destination"; - -template -struct destination_data : string_destination_data {}; - -template <> -struct destination_data : wstring_destination_data {}; +using TagTypes = ::testing::Types; +TYPED_TEST_CASE(MessageTest, TagTypes); + +TYPED_TEST(MessageTest, Constructors){ + network::basic_message message; // default construction + auto copy = message; // copy construction + ASSERT_TRUE(copy == message); +} -/** - * Defines a set of template functions that can be used to test - * generic code. - */ +TYPED_TEST(MessageTest, Equality) { + // Create an original message. + network::basic_message message; + message << network::source("source") << network::destination("destination") + << network::header("key", "value") << network::body("body"); -BOOST_AUTO_TEST_CASE_TEMPLATE(copy_constructor_test, T, tag_types) { - basic_message instance; - instance << header(header_name::string, header_value::string); - basic_message copy(instance); - BOOST_CHECK_EQUAL(headers(copy).count(header_name::string), - static_cast(1)); - typename headers_range >::type range = - headers(copy)[header_name::string]; - BOOST_CHECK(boost::begin(range) != boost::end(range)); -} + // Create the identical message. + network::basic_message other; + other << network::source("source") << network::destination("destination") + << network::header("key", "value") << network::body("body"); -BOOST_AUTO_TEST_CASE_TEMPLATE(swap_test, T, tag_types) { - basic_message instance; - instance << header(header_name::string, header_value::string); - basic_message other; - swap(instance, other); - BOOST_CHECK_EQUAL(headers(instance).count(header_name::string), - static_cast(0)); - BOOST_CHECK_EQUAL(headers(other).count(header_name::string), - static_cast(1)); + ASSERT_TRUE(message == other); } -BOOST_AUTO_TEST_CASE_TEMPLATE(headers_directive_test, T, tag_types) { - basic_message instance; - instance << header(header_name::string, header_value::string); - BOOST_CHECK_EQUAL(headers(instance).count(header_name::string), - static_cast(1)); - typename headers_range >::type range = - headers(instance)[header_name::string]; - BOOST_CHECK(boost::begin(range) != boost::end(range)); -} +TYPED_TEST(MessageTest, Inequality) { + // Create an original message. + network::basic_message message; + message << network::source("source") << network::destination("destination") + << network::header("key", "value") << network::body("body"); -BOOST_AUTO_TEST_CASE_TEMPLATE(body_directive_test, T, tag_types) { - basic_message instance; - instance << ::boost::network::body(body_data::string); - typename string::type body_string = body(instance); - BOOST_CHECK(body_string == body_data::string); -} + // Create a different message. + network::basic_message other; + other << network::source("source") << network::destination("destination") + << network::header("key", "value") << network::body("different body!"); -BOOST_AUTO_TEST_CASE_TEMPLATE(source_directive_test, T, tag_types) { - basic_message instance; - instance << ::boost::network::source(source_data::string); - typename string::type source_string = source(instance); - BOOST_CHECK(source_string == source_data::string); + ASSERT_FALSE(message == other); } -BOOST_AUTO_TEST_CASE_TEMPLATE(destination_directive_test, T, tag_types) { - basic_message instance; - instance << destination(destination_data::string); - BOOST_CHECK(destination(instance) == destination_data::string); -} +} // namespace -BOOST_AUTO_TEST_CASE_TEMPLATE(remove_header_directive_test, T, tag_types) { - basic_message instance; - instance << header(header_name::string, header_value::string) - << remove_header(header_name::string); - typename headers_range >::type range = headers(instance); - BOOST_CHECK(boost::begin(range) == boost::end(range)); -} diff --git a/libs/network/test/message_transform_test.cpp b/libs/network/test/message_transform_test.cpp index bab8c4ede..10971519b 100644 --- a/libs/network/test/message_transform_test.cpp +++ b/libs/network/test/message_transform_test.cpp @@ -1,39 +1,38 @@ -// Copyright Dean Michael Berris 2007. +// Copyright Dean Michael Berris 2007. +// Copyright 2015, Google, Inc. // Distributed under the Boost Software License, Version 1.0. -// (See accompanying file LICENSE_1_0.txt or copy at -// http://www.boost.org/LICENSE_1_0.txt) +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) -#define BOOST_TEST_MODULE message test -#include -#include -#include +#include #include +#include -BOOST_AUTO_TEST_CASE(message_transform_toupper) { +TEST(MessageTransformTest, TransformToUpper) { using namespace boost::network; message msg; msg << source("me"); - BOOST_CHECK_EQUAL(source(msg), "me"); + ASSERT_EQ("me",source(msg)); msg << transform(to_upper_, source_); - BOOST_CHECK_EQUAL(source(msg), "ME"); + ASSERT_EQ("ME",source(msg)); msg << destination("you"); - BOOST_CHECK_EQUAL(destination(msg), "you"); + ASSERT_EQ("you",destination(msg)); msg << transform(to_upper_, destination_); - BOOST_CHECK_EQUAL(destination(msg), "YOU"); + ASSERT_EQ("YOU",destination(msg)); } -BOOST_AUTO_TEST_CASE(message_transform_tolower) { +TEST(MessageTransformTest, TransformToLower) { using namespace boost::network; message msg; msg << source("ME"); - BOOST_CHECK_EQUAL(source(msg), "ME"); + ASSERT_EQ("ME",source(msg)); msg << transform(to_lower_, source_); - BOOST_CHECK_EQUAL(source(msg), "me"); + ASSERT_EQ("me",source(msg)); msg << destination("YOU"); - BOOST_CHECK_EQUAL(destination(msg), "YOU"); + ASSERT_EQ("YOU",destination(msg)); msg << transform(to_lower_, destination_); - BOOST_CHECK_EQUAL(destination(msg), "you"); + ASSERT_EQ("you",destination(msg)); } diff --git a/libs/network/test/utils_thread_pool.cpp b/libs/network/test/utils_thread_pool.cpp index f2e424996..6a0cec6b6 100644 --- a/libs/network/test/utils_thread_pool.cpp +++ b/libs/network/test/utils_thread_pool.cpp @@ -1,14 +1,13 @@ // Copyright 2010 Dean Michael Berris. +// Copyright 2015 Google, Inc. // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) -#define BOOST_TEST_MODULE utils thread pool test -#include -#include +#include +#include #include -#include using namespace boost::network; @@ -20,27 +19,42 @@ using namespace boost::network; // syntactically. // -BOOST_AUTO_TEST_CASE(default_constructor) { +TEST(ThreadPoolTest, DefaultConstructor) { utils::thread_pool pool; - BOOST_CHECK_EQUAL(pool.thread_count(), std::size_t(1)); + ASSERT_EQ(1, pool.thread_count()); } struct foo { foo() : val_(0) {} void bar(int val) { val_ += val; } - int const val() const { return val_; } + int val() const { return val_; } protected: int val_; }; -BOOST_AUTO_TEST_CASE(post_work) { +TEST(ThreadPoolTest, PostWork) { foo instance; { utils::thread_pool pool; - BOOST_CHECK_NO_THROW(pool.post(boost::bind(&foo::bar, &instance, 1))); - BOOST_CHECK_NO_THROW(pool.post(boost::bind(&foo::bar, &instance, 2))); + ASSERT_NO_THROW(pool.post([&instance] { instance.bar(1); })); + ASSERT_NO_THROW(pool.post([&instance] { instance.bar(2); })); // require that pool is destroyed here, RAII baby } - BOOST_CHECK_EQUAL(instance.val(), 3); + ASSERT_EQ(instance.val(), 3); +} + +// Test using multiple threads. +TEST(ThreadPoolTest, MultipleThreads) { + std::atomic counter{0}; + constexpr int64_t kMaxCount = 1e5; + { + utils::thread_pool pool(8); // nice round number of threads. + for (int64_t i = 0; i < kMaxCount; ++i) { + ASSERT_NO_THROW(pool.post( + [&counter] { counter.fetch_add(1, std::memory_order_acq_rel); })); + } + // Wait for threads to be done. + } + ASSERT_EQ(kMaxCount, counter.load(std::memory_order_acquire)); }