Skip to content

Commit

Permalink
style: Several small fixes to tests and documentation
Browse files Browse the repository at this point in the history
  • Loading branch information
Tradias committed Sep 8, 2024
1 parent ce77d98 commit 0809f3d
Show file tree
Hide file tree
Showing 10 changed files with 101 additions and 57 deletions.
23 changes: 19 additions & 4 deletions doc/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
find_package(Doxygen)

if(TARGET Doxygen::doxygen)
function(asio_grpc_doxygen)
if(NOT TARGET Doxygen::doxygen)
return()
endif()

set(DOXYGEN_PROJECT_NUMBER "v${PROJECT_VERSION}")
set(DOXYGEN_OUTPUT_DIRECTORY "${ASIO_GRPC_PROJECT_ROOT}")
set(DOXYGEN_SHOW_USED_FILES "NO")
Expand Down Expand Up @@ -28,11 +32,14 @@ if(TARGET Doxygen::doxygen)
# `doxygen -w html header.html delete_me.html delete_me.css`
set(DOXYGEN_HTML_HEADER "${CMAKE_CURRENT_LIST_DIR}/header.html")

set(DOXYGEN_DISABLE_INDEX "NO")
set(DOXYGEN_FULL_SIDEBAR "NO")
set(DOXYGEN_HTML_EXTRA_FILES "${CMAKE_CURRENT_LIST_DIR}/doxygen-awesome-darkmode-toggle.js")
set(DOXYGEN_HTML_EXTRA_STYLESHEET
"${CMAKE_CURRENT_LIST_DIR}/doxygen-awesome.css" "${CMAKE_CURRENT_LIST_DIR}/doxygen-awesome-sidebar-only.css"
"${CMAKE_CURRENT_LIST_DIR}/doxygen-awesome-sidebar-only-darkmode-toggle.css"
"${CMAKE_CURRENT_LIST_DIR}/custom.css")
set(HTML_COLORSTYLE "LIGHT")
set(DOXYGEN_HTML_COLORSTYLE_HUE "209")
set(DOXYGEN_HTML_COLORSTYLE_SAT "255")
set(DOXYGEN_HTML_COLORSTYLE_GAMMA "113")
Expand All @@ -42,9 +49,17 @@ if(TARGET Doxygen::doxygen)
set(DOXYGEN_TREEVIEW_WIDTH "310")
set(DOXYGEN_MACRO_EXPANSION "YES")
set(DOXYGEN_INCLUDE_PATH "${ASIO_GRPC_PROJECT_ROOT}/src" "${_VCPKG_INSTALLED_DIR}/${VCPKG_TARGET_TRIPLET}")
set(DOXYGEN_PREDEFINED "AGRPC_BOOST_ASIO" "AGRPC_GENERATING_DOCUMENTATION" "AGRPC_ASIO_HAS_CO_AWAIT"
"AGRPC_ASIO_HAS_CANCELLATION_SLOT")
set(DOXYGEN_PREDEFINED
"AGRPC_BOOST_ASIO"
"AGRPC_GENERATING_DOCUMENTATION"
"AGRPC_ASIO_HAS_CO_AWAIT"
"AGRPC_ASIO_HAS_CANCELLATION_SLOT"
"AGRPC_ASIO_HAS_BIND_ALLOCATOR"
"AGRPC_ASIO_HAS_NEW_SPAWN"
"AGRPC_ASIO_HAS_IMMEDIATE_EXECUTOR")
set(DOXYGEN_SKIP_FUNCTION_MACROS "NO")

doxygen_add_docs(asio-grpc-doxygen WORKING_DIRECTORY "${ASIO_GRPC_PROJECT_ROOT}")
endif()
endfunction()

asio_grpc_doxygen()
4 changes: 2 additions & 2 deletions example/snippets/client.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,8 @@ asio::awaitable<void> mock_stub(agrpc::GrpcContext& grpc_context)
const grpc::Status status =
co_await RPC::request(grpc_context, mock_stub, client_context, request, response, asio::use_awaitable);

assert(status.ok());
assert(42 == response.integer());
EXPECT_TRUE(status.ok());
EXPECT_EQ(42, response.integer());
/* [mock-stub] */
}

Expand Down
5 changes: 2 additions & 3 deletions example/snippets/server.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,17 +47,16 @@ void create_multi_threaded_server_grpc_context()
const auto concurrency = std::thread::hardware_concurrency();
grpc::ServerBuilder builder;
agrpc::GrpcContext grpc_context{builder.AddCompletionQueue(), concurrency};
auto guard = asio::make_work_guard(grpc_context);
// ... register services, start server
std::vector<std::thread> threads(concurrency);
for (auto& thread : threads)
{
thread = std::thread{[&]
{
// ... register rpc handlers
grpc_context.run();
}};
}
// ...
guard.reset();
for (auto& thread : threads)
{
thread.join();
Expand Down
6 changes: 5 additions & 1 deletion src/agrpc/detail/epilogue.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef AGRPC_GENERATING_DOCUMENTATION

// config.hpp
#undef AGRPC_UNLIKELY
#undef AGRPC_LIKELY
Expand All @@ -30,4 +32,6 @@
#undef AGRPC_ASIO_HAS_IMMEDIATE_EXECUTOR

// awaitable.hpp
#undef AGRPC_ASIO_HAS_CO_AWAIT
#undef AGRPC_ASIO_HAS_CO_AWAIT

#endif
3 changes: 2 additions & 1 deletion src/agrpc/test.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ AGRPC_NAMESPACE_BEGIN()
* @brief Test utility to manually process gRPC tags
*
* This function can be used to process gRPC tags in places where the tag does not go through the
* `grpc::CompletionQueue`, for example in mocked stubs. It processes the tag in a manner equivalent to `asio::post`.
* `grpc::CompletionQueue`, for example in mocked stubs. It processes the tag in a manner equivalent to `asio::post`
* while being compatible with GrpcContext.run/poll_completion_queue().
*
* Example using Google Mock:
*
Expand Down
2 changes: 1 addition & 1 deletion test/src/test_test_17.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@

TEST_CASE_FIXTURE(test::MockTest, "mock unary request")
{
auto mock_reader = test::set_up_unary_test(*this);
test::set_up_unary_test(*this);
test::spawn_and_run(grpc_context,
[&](auto&& yield)
{
Expand Down
67 changes: 39 additions & 28 deletions test/utils/utils/free_port.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,15 +32,32 @@ namespace
{
namespace fs = boost::filesystem;

constexpr auto PORT_FILE_NAME = "agrpcServerUsedTestPort";
constexpr uint16_t START_PORT = 16397u;
constexpr auto MAX_PORT_FILE_AGE = std::chrono::minutes(1);

void recreate_if_old(const fs::path& port_file)
template <class Function>
auto perform_under_global_lock(Function&& function)
{
static std::mutex function_mutex{};
std::lock_guard function_lock{function_mutex};
return std::forward<Function>(function)();
}

auto get_port_file_path() { return fs::temp_directory_path() / "agrpcServerUsedTestPort"; }

auto get_port_lock_file_path()
{
auto path = get_port_file_path();
path += ".lock";
return path;
}

void recreate_if_old(const fs::path& port_file, std::chrono::nanoseconds max_age)
{
if (fs::exists(port_file))
{
const auto last_write_time = std::chrono::system_clock::from_time_t(fs::last_write_time(port_file));
if (last_write_time + std::chrono::minutes(1) < std::chrono::system_clock::now())
if (last_write_time + max_age < std::chrono::system_clock::now())
{
fs::remove(port_file);
std::ofstream create_file{port_file.native()};
Expand All @@ -52,63 +69,57 @@ void recreate_if_old(const fs::path& port_file)
}
}

auto get_port_lock_file()
void create_file_if_not_exist(const fs::path& path)
{
auto port_file = fs::temp_directory_path() / (std::string(PORT_FILE_NAME) + ".lock");
std::ofstream file_stream{port_file.native()};
return port_file;
if (!fs::exists(path))
{
std::ofstream create_file{path.native()};
}
}

template <class Function>
auto perform_under_file_lock(Function&& function, const fs::path& lock_file)
auto perform_under_file_lock(Function&& function)
{
auto lock_file_name = lock_file.string();
static boost::interprocess::file_lock file_lock{lock_file_name.c_str()};
static boost::interprocess::file_lock file_lock{get_port_lock_file_path().string().c_str()};
boost::interprocess::scoped_lock<boost::interprocess::file_lock> scoped{file_lock};
return std::forward<Function>(function)();
}

auto read_and_increment_port(const fs::path& port_file)
auto read_and_increment_port(const fs::path& port_file, uint16_t start_port)
{
uint16_t port = START_PORT;
std::fstream file_stream{port_file.native(), std::ios::in | std::ios::out};
static constexpr auto MAX_DIGITS = std::numeric_limits<decltype(port)>::digits10 + 1;

static constexpr auto MAX_DIGITS = std::numeric_limits<uint16_t>::digits10 + 1;
std::array<char, MAX_DIGITS> file_content{};
file_stream.read(file_content.data(), file_content.size());

uint16_t port = start_port;
std::from_chars(file_content.data(), file_content.data() + file_content.size(), port);
++port;
const auto [end, _] = std::to_chars(file_content.data(), file_content.data() + file_content.size(), port);

file_stream.clear();
file_stream.seekp(std::ios::beg);
file_stream.write(file_content.data(), end - file_content.data());
return port;
}

template <class Function>
auto perform_under_mutex_lock(Function&& function)
{
static std::mutex function_mutex{};
std::lock_guard function_lock{function_mutex};
return std::forward<Function>(function)();
return port;
}
} // namespace

uint16_t get_free_port()
{
return perform_under_mutex_lock(
return perform_under_global_lock(
[]
{
static auto port_lock_file = get_port_lock_file();
const auto port_file = fs::temp_directory_path() / PORT_FILE_NAME;
const auto port_lock_file = get_port_lock_file_path();
create_file_if_not_exist(port_lock_file);
const auto port_file = get_port_file_path();
return perform_under_file_lock(
[&]
{
recreate_if_old(port_file);
return read_and_increment_port(port_file);
},
port_lock_file);
recreate_if_old(port_file, MAX_PORT_FILE_AGE);
return read_and_increment_port(port_file, START_PORT);
});
});
}
} // namespace test
4 changes: 3 additions & 1 deletion test/utils/utils/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@

#include <gmock/gmock.h>

#include <string_view>

#define DOCTEST_CONFIG_IMPLEMENT
#include <doctest/doctest.h>

Expand All @@ -33,7 +35,7 @@ int main(int argc, char** argv)
}
const char* file = result.file_name() ? result.file_name() : "unknown";
const int line = result.line_number() != -1 ? result.line_number() : 0;
const char* message = result.message() ? result.message() : "no message";
const std::string_view message = result.message() ? result.message() : "no message";
if (result.nonfatally_failed())
{
ADD_FAIL_CHECK_AT(file, line, message);
Expand Down
42 changes: 27 additions & 15 deletions test/utils/utils/test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@

namespace test
{
std::unique_ptr<testing::NiceMock<MockClientAsyncResponseReader>> set_up_unary_test(MockTest& test)
void set_up_unary_test(MockTest& test)
{
auto mock_reader = std::make_unique<testing::NiceMock<MockClientAsyncResponseReader>>();
EXPECT_CALL(*mock_reader, Finish)
Expand All @@ -28,26 +28,38 @@ std::unique_ptr<testing::NiceMock<MockClientAsyncResponseReader>> set_up_unary_t
response->set_integer(42);
agrpc::process_grpc_tag(test.grpc_context, tag, true);
});
EXPECT_CALL(test.stub, PrepareAsyncUnaryRaw).WillOnce(testing::Return(mock_reader.get()));
return mock_reader;
EXPECT_CALL(test.stub, PrepareAsyncUnaryRaw)
.WillOnce(
[reader = std::move(mock_reader)]
{
// GRPC wraps the return value into a unique_ptr with a specialized std::default_delete that does
// nothing
return reader.get();
});

Check failure on line 38 in test/utils/utils/test.cpp

View workflow job for this annotation

GitHub Actions / Ubuntu/20.04/Default

cannot convert ‘test::set_up_unary_test(test::MockTest&)::<lambda()>’ to ‘const testing::Action<grpc::ClientAsyncResponseReaderInterface<test::msg::Response>*(grpc::ClientContext*, const test::msg::Request&, grpc::CompletionQueue*)>&’

Check failure on line 38 in test/utils/utils/test.cpp

View workflow job for this annotation

GitHub Actions / Ubuntu/20.04/Default

cannot convert ‘test::set_up_unary_test(test::MockTest&)::<lambda()>’ to ‘const testing::Action<grpc::ClientAsyncResponseReaderInterface<test::msg::Response>*(grpc::ClientContext*, const test::msg::Request&, grpc::CompletionQueue*)>&’

Check failure on line 38 in test/utils/utils/test.cpp

View workflow job for this annotation

GitHub Actions / Ubuntu/20.04/Default

cannot convert ‘test::set_up_unary_test(test::MockTest&)::<lambda()>’ to ‘const testing::Action<grpc::ClientAsyncResponseReaderInterface<test::msg::Response>*(grpc::ClientContext*, const test::msg::Request&, grpc::CompletionQueue*)>&’

Check failure on line 38 in test/utils/utils/test.cpp

View workflow job for this annotation

GitHub Actions / Ubuntu/20.04/Default

cannot convert ‘test::set_up_unary_test(test::MockTest&)::<lambda()>’ to ‘const testing::Action<grpc::ClientAsyncResponseReaderInterface<test::msg::Response>*(grpc::ClientContext*, const test::msg::Request&, grpc::CompletionQueue*)>&’
}

void set_up_server_streaming_test(MockTest& test)
{
auto mock_reader = std::make_unique<testing::NiceMock<MockClientAsyncReader>>();
EXPECT_CALL(*mock_reader, Read)
EXPECT_CALL(test.stub, PrepareAsyncServerStreamingRaw)
.WillOnce(
[&](test::msg::Response* response, void* tag)
[&]
{
response->set_integer(42);
agrpc::process_grpc_tag(test.grpc_context, tag, true);
});
EXPECT_CALL(*mock_reader, StartCall)
.WillOnce(
[&](void* tag)
{
agrpc::process_grpc_tag(test.grpc_context, tag, true);
auto mock_reader = std::make_unique<testing::NiceMock<MockClientAsyncReader>>();
EXPECT_CALL(*mock_reader, Read)
.WillOnce(
[&](test::msg::Response* response, void* tag)
{
response->set_integer(42);
agrpc::process_grpc_tag(test.grpc_context, tag, true);
});
EXPECT_CALL(*mock_reader, StartCall)
.WillOnce(
[&](void* tag)
{
agrpc::process_grpc_tag(test.grpc_context, tag, true);
});
// GRPC wraps the return value into a unique_ptr
return mock_reader.release();
});

Check failure on line 63 in test/utils/utils/test.cpp

View workflow job for this annotation

GitHub Actions / Ubuntu/20.04/Default

cannot convert ‘test::set_up_server_streaming_test(test::MockTest&)::<lambda()>’ to ‘const testing::Action<grpc::ClientAsyncReaderInterface<test::msg::Response>*(grpc::ClientContext*, const test::msg::Request&, grpc::CompletionQueue*)>&’

Check failure on line 63 in test/utils/utils/test.cpp

View workflow job for this annotation

GitHub Actions / Ubuntu/20.04/Default

cannot convert ‘test::set_up_server_streaming_test(test::MockTest&)::<lambda()>’ to ‘const testing::Action<grpc::ClientAsyncReaderInterface<test::msg::Response>*(grpc::ClientContext*, const test::msg::Request&, grpc::CompletionQueue*)>&’

Check failure on line 63 in test/utils/utils/test.cpp

View workflow job for this annotation

GitHub Actions / Ubuntu/20.04/Default

cannot convert ‘test::set_up_server_streaming_test(test::MockTest&)::<lambda()>’ to ‘const testing::Action<grpc::ClientAsyncReaderInterface<test::msg::Response>*(grpc::ClientContext*, const test::msg::Request&, grpc::CompletionQueue*)>&’

Check failure on line 63 in test/utils/utils/test.cpp

View workflow job for this annotation

GitHub Actions / Ubuntu/20.04/Default

cannot convert ‘test::set_up_server_streaming_test(test::MockTest&)::<lambda()>’ to ‘const testing::Action<grpc::ClientAsyncReaderInterface<test::msg::Response>*(grpc::ClientContext*, const test::msg::Request&, grpc::CompletionQueue*)>&’
EXPECT_CALL(test.stub, PrepareAsyncServerStreamingRaw).WillOnce(testing::Return(mock_reader.release()));
}
} // namespace test
2 changes: 1 addition & 1 deletion test/utils/utils/test.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ struct MockClientAsyncReader : grpc::ClientAsyncReaderInterface<test::msg::Respo
MOCK_METHOD2(Read, void(test::msg::Response*, void*));
};

std::unique_ptr<testing::NiceMock<MockClientAsyncResponseReader>> set_up_unary_test(MockTest& test);
void set_up_unary_test(MockTest& test);

void set_up_server_streaming_test(MockTest& test);
} // namespace test
Expand Down

0 comments on commit 0809f3d

Please sign in to comment.