ILogStuff - enabling logging from `connection_impl` and transports
Note that a side effect of this change is that we now will be able to raise events from transports since transports now will have a (smart) pointer to the connection_impl instance.
This commit is contained in:
Родитель
ac5a9e0a4d
Коммит
78ee26b990
|
@ -7,6 +7,9 @@
|
|||
#include "_exports.h"
|
||||
#include "transport_type.h"
|
||||
#include "connection_state.h"
|
||||
#include "trace_level.h"
|
||||
#include "log_writer.h"
|
||||
#include "trace_log_writer.h"
|
||||
|
||||
namespace signalr
|
||||
{
|
||||
|
@ -15,7 +18,8 @@ namespace signalr
|
|||
class connection
|
||||
{
|
||||
public:
|
||||
explicit connection(const utility::string_t& url, const utility::string_t& querystring = U(""));
|
||||
explicit connection(const utility::string_t& url, const utility::string_t& querystring = U(""),
|
||||
trace_level trace_level = trace_level::all, std::shared_ptr<log_writer> log_writer = std::make_shared<trace_log_writer>());
|
||||
|
||||
// TODO: consider making connection class copyable to enable passing by value
|
||||
connection(const connection&) = delete;
|
||||
|
|
|
@ -8,8 +8,8 @@
|
|||
|
||||
namespace signalr
|
||||
{
|
||||
connection::connection(const utility::string_t& url, const utility::string_t& querystring)
|
||||
: m_pImpl(connection_impl::create(url, querystring))
|
||||
connection::connection(const utility::string_t& url, const utility::string_t& querystring, trace_level trace_level, std::shared_ptr<log_writer> log_writer)
|
||||
: m_pImpl(connection_impl::create(url, querystring, trace_level, log_writer))
|
||||
{}
|
||||
|
||||
// Do NOT remove this destructor. Letting the compiler generate and inline the default dtor may lead to
|
||||
|
|
|
@ -8,20 +8,21 @@
|
|||
|
||||
namespace signalr
|
||||
{
|
||||
std::shared_ptr<connection_impl> connection_impl::create(const utility::string_t& url, const utility::string_t& querystring)
|
||||
{
|
||||
return connection_impl::create(url, querystring, std::make_unique<web_request_factory>(), std::make_unique<transport_factory>());
|
||||
}
|
||||
|
||||
std::shared_ptr<connection_impl> connection_impl::create(const utility::string_t& url, const utility::string_t& querystring,
|
||||
std::unique_ptr<web_request_factory> web_request_factory, std::unique_ptr<transport_factory> transport_factory)
|
||||
trace_level trace_level, std::shared_ptr<log_writer> log_writer)
|
||||
{
|
||||
return std::shared_ptr<connection_impl>(new connection_impl(url, querystring, std::move(web_request_factory), std::move(transport_factory)));
|
||||
return connection_impl::create(url, querystring, trace_level, log_writer, std::make_unique<web_request_factory>(), std::make_unique<transport_factory>());
|
||||
}
|
||||
|
||||
connection_impl::connection_impl(const utility::string_t& url, const utility::string_t& querystring,
|
||||
std::shared_ptr<connection_impl> connection_impl::create(const utility::string_t& url, const utility::string_t& querystring, trace_level trace_level,
|
||||
std::shared_ptr<log_writer> log_writer, std::unique_ptr<web_request_factory> web_request_factory, std::unique_ptr<transport_factory> transport_factory)
|
||||
{
|
||||
return std::shared_ptr<connection_impl>(new connection_impl(url, querystring, trace_level, log_writer, std::move(web_request_factory), std::move(transport_factory)));
|
||||
}
|
||||
|
||||
connection_impl::connection_impl(const utility::string_t& url, const utility::string_t& querystring, trace_level trace_level, std::shared_ptr<log_writer> log_writer,
|
||||
std::unique_ptr<web_request_factory> web_request_factory, std::unique_ptr<transport_factory> transport_factory)
|
||||
: m_base_uri(url), m_querystring(querystring), m_web_request_factory(std::move(web_request_factory)),
|
||||
: m_base_uri(url), m_querystring(querystring), m_logger(log_writer, trace_level), m_web_request_factory(std::move(web_request_factory)),
|
||||
m_transport_factory(std::move(transport_factory)), m_connection_state(std::move(connection_state::disconnected))
|
||||
{ }
|
||||
|
||||
|
@ -41,17 +42,47 @@ namespace signalr
|
|||
return m_connection_state.load();
|
||||
}
|
||||
|
||||
logger connection_impl::get_logger() const
|
||||
{
|
||||
return m_logger;
|
||||
}
|
||||
|
||||
bool connection_impl::change_state(connection_state old_state, connection_state new_state)
|
||||
{
|
||||
connection_state expected_state{ old_state };
|
||||
|
||||
if (!m_connection_state.compare_exchange_strong(expected_state, new_state, std::memory_order_seq_cst))
|
||||
if (m_connection_state.compare_exchange_strong(expected_state, new_state, std::memory_order_seq_cst))
|
||||
{
|
||||
// TODO: add logging
|
||||
m_logger.log(
|
||||
trace_level::state_changes,
|
||||
utility::string_t(_XPLATSTR("state changed: "))
|
||||
.append(translate_connection_state(old_state))
|
||||
.append(_XPLATSTR(" -> "))
|
||||
.append(translate_connection_state(new_state)));
|
||||
|
||||
// TODO: invoke state_changed callback
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
utility::string_t connection_impl::translate_connection_state(connection_state state)
|
||||
{
|
||||
switch (state)
|
||||
{
|
||||
case connection_state::connecting:
|
||||
return _XPLATSTR("connecting");
|
||||
case connection_state::connected:
|
||||
return _XPLATSTR("connected");
|
||||
case connection_state::reconnecting:
|
||||
return _XPLATSTR("reconnecting");
|
||||
case connection_state::disconnected:
|
||||
return _XPLATSTR("disconnected");
|
||||
default:
|
||||
_ASSERTE(false);
|
||||
return _XPLATSTR("(unknown)");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -21,10 +21,11 @@ namespace signalr
|
|||
class connection_impl : public std::enable_shared_from_this<connection_impl>
|
||||
{
|
||||
public:
|
||||
static std::shared_ptr<connection_impl> create(const utility::string_t& url, const utility::string_t& querystring);
|
||||
|
||||
static std::shared_ptr<connection_impl> create(const utility::string_t& url, const utility::string_t& querystring,
|
||||
std::unique_ptr<web_request_factory> web_request_factory, std::unique_ptr<transport_factory> transport_factory);
|
||||
trace_level trace_level, std::shared_ptr<log_writer> log_writer);
|
||||
|
||||
static std::shared_ptr<connection_impl> create(const utility::string_t& url, const utility::string_t& querystring, trace_level trace_level,
|
||||
std::shared_ptr<log_writer> log_writer, std::unique_ptr<web_request_factory> web_request_factory, std::unique_ptr<transport_factory> transport_factory);
|
||||
|
||||
connection_impl(const connection_impl&) = delete;
|
||||
|
||||
|
@ -34,17 +35,21 @@ namespace signalr
|
|||
|
||||
connection_state get_connection_state() const;
|
||||
|
||||
logger get_logger() const;
|
||||
|
||||
private:
|
||||
web::uri m_base_uri;
|
||||
utility::string_t m_querystring;
|
||||
std::atomic<connection_state> m_connection_state;
|
||||
logger m_logger;
|
||||
|
||||
std::unique_ptr<web_request_factory> m_web_request_factory;
|
||||
std::unique_ptr<transport_factory> m_transport_factory;
|
||||
|
||||
connection_impl(const utility::string_t& url, const utility::string_t& querystring,
|
||||
connection_impl(const utility::string_t& url, const utility::string_t& querystring, trace_level trace_level, std::shared_ptr<log_writer> log_writer,
|
||||
std::unique_ptr<web_request_factory> web_request_factory, std::unique_ptr<transport_factory> transport_factory);
|
||||
|
||||
bool change_state(connection_state old_state, connection_state new_state);
|
||||
utility::string_t translate_connection_state(connection_state state);
|
||||
};
|
||||
}
|
||||
|
|
|
@ -16,9 +16,21 @@ namespace signalr
|
|||
{
|
||||
if ((level & m_trace_level) != trace_level::none)
|
||||
{
|
||||
utility::ostringstream_t os;
|
||||
os << utility::datetime::utc_now().to_string(utility::datetime::date_format::ISO_8601) << _XPLATSTR(" ") << entry << std::endl;
|
||||
m_log_writer->write(os.str());
|
||||
try
|
||||
{
|
||||
utility::ostringstream_t os;
|
||||
os << utility::datetime::utc_now().to_string(utility::datetime::date_format::ISO_8601) << _XPLATSTR(" ") << entry << std::endl;
|
||||
m_log_writer->write(os.str());
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
ucerr << _XPLATSTR("error occurred when logging: ") << utility::conversions::to_string_t(e.what())
|
||||
<< std::endl << _XPLATSTR(" entry: ") << entry << std::endl;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
ucerr << _XPLATSTR("unknown error occurred when logging") << std::endl << _XPLATSTR(" entry: ") << entry << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -4,14 +4,15 @@
|
|||
#include "stdafx.h"
|
||||
#include "transport_factory.h"
|
||||
#include "websocket_transport.h"
|
||||
#include "connection_impl.h"
|
||||
|
||||
namespace signalr
|
||||
{
|
||||
std::unique_ptr<transport> transport_factory::create_transport(transport_type transport_type)
|
||||
std::unique_ptr<transport> transport_factory::create_transport(transport_type transport_type, std::shared_ptr<connection_impl> connection)
|
||||
{
|
||||
if (transport_type == transport_type::websockets)
|
||||
{
|
||||
return std::make_unique<websocket_transport>(std::make_unique<default_websocket_client>());
|
||||
return std::make_unique<websocket_transport>(std::make_unique<default_websocket_client>(), connection);
|
||||
}
|
||||
|
||||
throw std::exception("not implemented");
|
||||
|
|
|
@ -9,10 +9,12 @@
|
|||
|
||||
namespace signalr
|
||||
{
|
||||
class connection_impl;
|
||||
|
||||
class transport_factory
|
||||
{
|
||||
public:
|
||||
std::unique_ptr<transport> virtual create_transport(transport_type transport_type);
|
||||
virtual std::unique_ptr<transport> create_transport(transport_type transport_type, std::shared_ptr<connection_impl> connection);
|
||||
|
||||
virtual ~transport_factory();
|
||||
};
|
||||
|
|
|
@ -3,16 +3,17 @@
|
|||
|
||||
#include "stdafx.h"
|
||||
#include "websocket_transport.h"
|
||||
#include "logger.h"
|
||||
|
||||
namespace signalr
|
||||
{
|
||||
namespace
|
||||
{
|
||||
void receive_loop(std::shared_ptr<websocket_client> websocket_client, pplx::cancellation_token_source cts);
|
||||
void receive_loop(std::shared_ptr<websocket_client> websocket_client, std::shared_ptr<connection_impl> connection, pplx::cancellation_token_source cts);
|
||||
}
|
||||
|
||||
websocket_transport::websocket_transport(std::shared_ptr<websocket_client> websocket_client)
|
||||
: m_websocket_client(websocket_client)
|
||||
websocket_transport::websocket_transport(std::shared_ptr<websocket_client> websocket_client, std::shared_ptr<connection_impl> connection)
|
||||
: m_websocket_client(websocket_client), m_connection(connection)
|
||||
{
|
||||
// we use this cts to check if the receive loop is running so it should be
|
||||
// initially cancelled to indicate that the receive loop is not running
|
||||
|
@ -41,21 +42,27 @@ namespace signalr
|
|||
pplx::cancellation_token_source receive_loop_cts;
|
||||
std::shared_ptr<websocket_client> websocket_client(m_websocket_client);
|
||||
pplx::task_completion_event<void> connect_tce;
|
||||
auto connection = m_connection;
|
||||
|
||||
m_websocket_client->connect(url)
|
||||
.then([websocket_client, connect_tce, receive_loop_cts](pplx::task<void> connect_task)
|
||||
.then([websocket_client, connect_tce, receive_loop_cts, connection](pplx::task<void> connect_task)
|
||||
{
|
||||
try
|
||||
{
|
||||
connect_task.get();
|
||||
receive_loop(websocket_client, receive_loop_cts);
|
||||
receive_loop(websocket_client, connection, receive_loop_cts);
|
||||
connect_tce.set();
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
// TODO: logging, on error(?) - see what we do in the .net client
|
||||
connection->get_logger().log(
|
||||
trace_level::messages,
|
||||
utility::string_t(_XPLATSTR("[websocket transport] exception when connecting to the server: "))
|
||||
.append(utility::conversions::to_string_t(e.what())));
|
||||
|
||||
// TODO: on error(?) - see what we do in the .net client
|
||||
receive_loop_cts.cancel();
|
||||
connect_tce.set_exception(e);
|
||||
connect_tce.set_exception(std::current_exception());
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -74,27 +81,36 @@ namespace signalr
|
|||
{
|
||||
m_receive_loop_cts.cancel();
|
||||
|
||||
auto logger = m_connection->get_logger();
|
||||
|
||||
return m_websocket_client->close()
|
||||
.then([](pplx::task<void> close_task)
|
||||
{
|
||||
.then([logger](pplx::task<void> close_task)
|
||||
mutable {
|
||||
try
|
||||
{
|
||||
close_task.get();
|
||||
}
|
||||
catch (const std::exception &)
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
// TODO:log
|
||||
logger.log(
|
||||
trace_level::messages,
|
||||
utility::string_t(_XPLATSTR("[websocket transport] exception when closing websocket: "))
|
||||
.append(utility::conversions::to_string_t(e.what())));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// unnamed namespace makes this function invisible for other translation units
|
||||
// unnamed namespace makes this function invisible/unusable in other translation units
|
||||
namespace
|
||||
{
|
||||
void receive_loop(std::shared_ptr<websocket_client> websocket_client, pplx::cancellation_token_source cts)
|
||||
void receive_loop(std::shared_ptr<websocket_client> websocket_client, std::shared_ptr<connection_impl> connection, pplx::cancellation_token_source cts)
|
||||
{
|
||||
websocket_client->receive()
|
||||
.then([websocket_client, cts](pplx::task<std::string> receive_task)
|
||||
// There are two cases when we exit the loop. The first case is implicit - we pass the cancellation_token
|
||||
// to `then` (note this is after the lambda body) and if the token is cancelled the continuation will not
|
||||
// run at all. The second - explicit - case happens if the token gets cancelled after the continuation has
|
||||
// been started in which case we just stop the loop by not scheduling another receive task.
|
||||
.then([websocket_client, connection, cts](pplx::task<std::string> receive_task)
|
||||
{
|
||||
try
|
||||
{
|
||||
|
@ -104,29 +120,41 @@ namespace signalr
|
|||
|
||||
if (!pplx::is_task_cancellation_requested())
|
||||
{
|
||||
receive_loop(websocket_client, cts);
|
||||
receive_loop(websocket_client, connection, cts);
|
||||
}
|
||||
|
||||
// TODO: `else` to log that loop has been cancelled?
|
||||
|
||||
return;
|
||||
}
|
||||
// TODO: log, report error, close websocket (when appropriate)
|
||||
catch (const web_sockets::client::websocket_exception&)
|
||||
// TODO: report error, close websocket (when appropriate)
|
||||
catch (const web_sockets::client::websocket_exception& e)
|
||||
{
|
||||
connection->get_logger().log(
|
||||
trace_level::messages,
|
||||
utility::string_t(_XPLATSTR("[websocket transport] websocket exception when receiving data: "))
|
||||
.append(utility::conversions::to_string_t(e.what())));
|
||||
}
|
||||
catch (const pplx::task_canceled &)
|
||||
catch (const pplx::task_canceled& e)
|
||||
{
|
||||
connection->get_logger().log(
|
||||
trace_level::messages,
|
||||
utility::string_t(_XPLATSTR("[websocket transport] receive task cancelled: "))
|
||||
.append(utility::conversions::to_string_t(e.what())));
|
||||
}
|
||||
catch (const std::exception&)
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
connection->get_logger().log(
|
||||
trace_level::messages,
|
||||
utility::string_t(_XPLATSTR("[websocket transport] error receiving response from websocket: "))
|
||||
.append(utility::conversions::to_string_t(e.what())));
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
connection->get_logger().log(
|
||||
trace_level::messages,
|
||||
utility::string_t(_XPLATSTR("[websocket transport] unknown error occurred when receiving response from websocket")));
|
||||
}
|
||||
|
||||
cts.cancel();
|
||||
|
||||
}, cts.get_token());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,14 +8,14 @@
|
|||
#include "transport.h"
|
||||
#include "logger.h"
|
||||
#include "default_websocket_client.h"
|
||||
#include "connection_impl.h"
|
||||
|
||||
namespace signalr
|
||||
{
|
||||
class websocket_transport : public transport
|
||||
{
|
||||
public:
|
||||
|
||||
websocket_transport(std::shared_ptr<websocket_client> websocket_client);
|
||||
websocket_transport(std::shared_ptr<websocket_client> websocket_client, std::shared_ptr<connection_impl> connection);
|
||||
|
||||
~websocket_transport();
|
||||
|
||||
|
@ -31,6 +31,7 @@ namespace signalr
|
|||
|
||||
private:
|
||||
std::shared_ptr<websocket_client> m_websocket_client;
|
||||
std::shared_ptr<connection_impl> m_connection;
|
||||
|
||||
pplx::cancellation_token_source m_receive_loop_cts;
|
||||
};
|
||||
|
|
|
@ -88,6 +88,7 @@
|
|||
<ClInclude Include="..\..\memory_log_writer.h" />
|
||||
<ClInclude Include="..\..\stdafx.h" />
|
||||
<ClInclude Include="..\..\targetver.h" />
|
||||
<ClInclude Include="..\..\test_utils.h" />
|
||||
<ClInclude Include="..\..\test_websocket_client.h" />
|
||||
<ClInclude Include="..\..\test_web_request_factory.h" />
|
||||
<ClInclude Include="..\..\web_request_stub.h" />
|
||||
|
@ -103,6 +104,7 @@
|
|||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Create</PrecompiledHeader>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\test_utils.cpp" />
|
||||
<ClCompile Include="..\..\test_websocket_client.cpp" />
|
||||
<ClCompile Include="..\..\test_web_request_factory.cpp" />
|
||||
<ClCompile Include="..\..\url_builder_tests.cpp" />
|
||||
|
|
|
@ -33,6 +33,9 @@
|
|||
<ClInclude Include="..\..\memory_log_writer.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\test_utils.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="..\..\stdafx.cpp">
|
||||
|
@ -74,6 +77,9 @@
|
|||
<ClCompile Include="..\..\memory_log_writer.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\test_utils.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "test_utils.h"
|
||||
#include "connection_impl.h"
|
||||
#include "signalrclient\trace_level.h"
|
||||
#include "signalrclient\trace_log_writer.h"
|
||||
|
@ -13,14 +14,17 @@ using namespace signalr;
|
|||
|
||||
TEST(connection_impl_connection_state, initial_connection_state_is_disconnected)
|
||||
{
|
||||
auto connection = connection_impl::create(_XPLATSTR("url"), _XPLATSTR(""));
|
||||
auto connection =
|
||||
connection_impl::create(_XPLATSTR("url"), _XPLATSTR(""), trace_level::none, std::make_shared<trace_log_writer>());
|
||||
|
||||
ASSERT_EQ(connection_state::disconnected, connection->get_connection_state());
|
||||
}
|
||||
|
||||
TEST(connection_impl_start, cannot_start_non_disconnected_exception)
|
||||
{
|
||||
auto connection = connection_impl::create(_XPLATSTR("url"), _XPLATSTR(""));
|
||||
auto connection =
|
||||
connection_impl::create(_XPLATSTR("url"), _XPLATSTR(""), trace_level::none, std::make_shared<trace_log_writer>());
|
||||
|
||||
connection->start().wait();
|
||||
|
||||
try
|
||||
|
@ -32,14 +36,31 @@ TEST(connection_impl_start, cannot_start_non_disconnected_exception)
|
|||
{
|
||||
ASSERT_EQ(
|
||||
_XPLATSTR("cannot start a connection that is not in the disconnected state"),
|
||||
utility::conversions::print_string(e.what()));
|
||||
utility::conversions::to_string_t(e.what()));
|
||||
}
|
||||
}
|
||||
|
||||
TEST(connection_impl_start, connection_state_is_connecting_when_connection_is_being_started)
|
||||
{
|
||||
auto connection = connection_impl::create(_XPLATSTR("url"), _XPLATSTR(""));
|
||||
auto connection =
|
||||
connection_impl::create(_XPLATSTR("url"), _XPLATSTR(""), trace_level::none, std::make_shared<trace_log_writer>());
|
||||
|
||||
connection->start().wait();
|
||||
ASSERT_EQ(connection->get_connection_state(), connection_state::connecting);
|
||||
}
|
||||
|
||||
TEST(connection_impl_change_state, change_state_logs)
|
||||
{
|
||||
std::shared_ptr<log_writer> writer(std::make_shared<memory_log_writer>());
|
||||
|
||||
auto connection =
|
||||
connection_impl::create(_XPLATSTR("url"), _XPLATSTR(""), trace_level::state_changes, writer);
|
||||
|
||||
connection->start().wait();
|
||||
|
||||
auto log_entries = std::dynamic_pointer_cast<memory_log_writer>(writer)->get_log_entries();
|
||||
ASSERT_FALSE(log_entries.empty());
|
||||
|
||||
auto entry = remove_date_from_log_entry(log_entries[0]);
|
||||
ASSERT_EQ(_XPLATSTR("state changed: disconnected -> connecting\n"), entry);
|
||||
}
|
|
@ -31,7 +31,7 @@ TEST(http_sender_get_response, exception_thrown_if_status_code_not_200)
|
|||
{
|
||||
ASSERT_EQ(
|
||||
_XPLATSTR("web exception: 404 ") + response_phrase,
|
||||
utility::conversions::print_string(e.what()));
|
||||
utility::conversions::to_string_t(e.what()));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "test_utils.h"
|
||||
#include <cpprest\asyncrt_utils.h>
|
||||
#include "signalrclient\trace_log_writer.h"
|
||||
#include "logger.h"
|
||||
|
@ -29,7 +30,7 @@ TEST(logger_write, entry_not_added_if_trace_level_not_set)
|
|||
|
||||
auto log_entries = std::dynamic_pointer_cast<memory_log_writer>(writer)->get_log_entries();
|
||||
|
||||
ASSERT_EQ(0, log_entries.size());
|
||||
ASSERT_TRUE(log_entries.empty());
|
||||
}
|
||||
|
||||
TEST(logger_write, entries_added_for_combined_trace_level)
|
||||
|
@ -53,11 +54,14 @@ TEST(logger_write, entries_formatted_correctly)
|
|||
logger l(writer, trace_level::all);
|
||||
l.log(trace_level::messages, _XPLATSTR("message"));
|
||||
|
||||
auto entry = std::dynamic_pointer_cast<memory_log_writer>(writer)->get_log_entries()[0];
|
||||
auto log_entries = std::dynamic_pointer_cast<memory_log_writer>(writer)->get_log_entries();
|
||||
ASSERT_FALSE(log_entries.empty());
|
||||
|
||||
auto date_str = entry.substr(0, 28);
|
||||
auto entry = log_entries[0];
|
||||
|
||||
auto date_str = entry.substr(0, entry.find_first_of(_XPLATSTR("Z")) + 1);
|
||||
auto date = utility::datetime::from_string(date_str, utility::datetime::ISO_8601);
|
||||
ASSERT_EQ(date_str, date.to_string(utility::datetime::ISO_8601));
|
||||
|
||||
ASSERT_EQ(_XPLATSTR(" message\n"), entry.substr(28));
|
||||
ASSERT_EQ(_XPLATSTR("message\n"), remove_date_from_log_entry(entry));
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "cpprest\basic_types.h"
|
||||
|
||||
utility::string_t remove_date_from_log_entry(const utility::string_t &log_entry)
|
||||
{
|
||||
// dates are ISO 8601 (e.g. `2014-11-13T06:05:29.452066Z`)
|
||||
auto date_end_index = log_entry.find_first_of(_XPLATSTR("Z")) + 1;
|
||||
|
||||
// date is followed by a whitespace hence +1
|
||||
return log_entry.substr(date_end_index + 1);
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cpprest\basic_types.h>
|
||||
|
||||
utility::string_t remove_date_from_log_entry(const utility::string_t &log_entry);
|
|
@ -2,8 +2,11 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "test_utils.h"
|
||||
#include "signalrclient\trace_log_writer.h"
|
||||
#include "test_websocket_client.h"
|
||||
#include "websocket_transport.h"
|
||||
#include "memory_log_writer.h"
|
||||
|
||||
using namespace signalr;
|
||||
using namespace web::experimental;
|
||||
|
@ -26,7 +29,8 @@ TEST(websocket_transport_connect, connect_connects_and_starts_receive_loop)
|
|||
return pplx::task_from_result(std::string(""));
|
||||
});
|
||||
|
||||
websocket_transport ws_transport(client);
|
||||
websocket_transport ws_transport{ client, connection_impl::create(_XPLATSTR("http://fake.uri"), _XPLATSTR(""),
|
||||
trace_level::none, std::make_shared<trace_log_writer>()) };
|
||||
|
||||
ws_transport.connect(_XPLATSTR("http://fakeuri.org")).get();
|
||||
|
||||
|
@ -37,28 +41,61 @@ TEST(websocket_transport_connect, connect_connects_and_starts_receive_loop)
|
|||
TEST(websocket_transport_connect, connect_propagates_exceptions)
|
||||
{
|
||||
auto client = std::make_shared<test_websocket_client>();
|
||||
client->set_connect_function([](const web::uri &) -> pplx::task<void>
|
||||
client->set_connect_function([](const web::uri &)->pplx::task<void>
|
||||
{
|
||||
throw web_sockets::client::websocket_exception(_XPLATSTR("connecting failed"));
|
||||
return pplx::task_from_exception<void>(web_sockets::client::websocket_exception(_XPLATSTR("connecting failed")));
|
||||
});
|
||||
|
||||
websocket_transport ws_transport(client);
|
||||
websocket_transport ws_transport{ client, connection_impl::create(_XPLATSTR("http://fake.uri"),
|
||||
_XPLATSTR(""), trace_level::none, std::make_shared<trace_log_writer>()) };
|
||||
|
||||
try
|
||||
{
|
||||
ws_transport.connect(_XPLATSTR("http://fakeuri.org")).wait();
|
||||
ws_transport.connect(_XPLATSTR("http://fakeuri.org")).get();
|
||||
ASSERT_TRUE(false); // exception not thrown
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
ASSERT_STREQ("connecting failed", e.what());
|
||||
ASSERT_EQ(_XPLATSTR("connecting failed"), utility::conversions::to_string_t(e.what()));
|
||||
}
|
||||
}
|
||||
|
||||
TEST(websocket_transport_connect, connect_logs_exceptions)
|
||||
{
|
||||
auto client = std::make_shared<test_websocket_client>();
|
||||
client->set_connect_function([](const web::uri &) -> pplx::task<void>
|
||||
{
|
||||
return pplx::task_from_exception<void>(web_sockets::client::websocket_exception(_XPLATSTR("connecting failed")));
|
||||
});
|
||||
|
||||
std::shared_ptr<log_writer> writer(std::make_shared<memory_log_writer>());
|
||||
|
||||
websocket_transport ws_transport{ client, connection_impl::create(_XPLATSTR("http://fake.uri"),
|
||||
_XPLATSTR(""), trace_level::messages, writer) };
|
||||
|
||||
try
|
||||
{
|
||||
ws_transport.connect(_XPLATSTR("http://fakeuri.org")).wait();
|
||||
}
|
||||
catch (...)
|
||||
{ }
|
||||
|
||||
auto log_entries = std::dynamic_pointer_cast<memory_log_writer>(writer)->get_log_entries();
|
||||
|
||||
ASSERT_FALSE(log_entries.empty());
|
||||
|
||||
auto entry = remove_date_from_log_entry(log_entries[0]);
|
||||
|
||||
ASSERT_EQ(
|
||||
_XPLATSTR("[websocket transport] exception when connecting to the server: connecting failed\n"),
|
||||
entry);
|
||||
}
|
||||
|
||||
TEST(websocket_transport_connect, cannot_call_connect_on_already_connected_transport)
|
||||
{
|
||||
auto client = std::make_shared<test_websocket_client>();
|
||||
websocket_transport ws_transport(client);
|
||||
websocket_transport ws_transport{ client, connection_impl::create(_XPLATSTR("http://fake.uri"),
|
||||
_XPLATSTR(""), trace_level::none, std::make_shared<trace_log_writer>()) };
|
||||
|
||||
ws_transport.connect(_XPLATSTR("http://fakeuri.org")).wait();
|
||||
|
||||
|
@ -69,7 +106,7 @@ TEST(websocket_transport_connect, cannot_call_connect_on_already_connected_trans
|
|||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
ASSERT_STREQ("transport already connected", e.what());
|
||||
ASSERT_EQ(_XPLATSTR("transport already connected"), utility::conversions::to_string_t(e.what()));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -77,7 +114,8 @@ TEST(websocket_transport_connect, can_connect_after_disconnecting)
|
|||
{
|
||||
|
||||
auto client = std::make_shared<test_websocket_client>();
|
||||
websocket_transport ws_transport(client);
|
||||
websocket_transport ws_transport{ client, connection_impl::create(_XPLATSTR("http://fake.uri"),
|
||||
_XPLATSTR(""), trace_level::none, std::make_shared<trace_log_writer>()) };
|
||||
|
||||
ws_transport.connect(_XPLATSTR("http://fakeuri.org")).get();
|
||||
ws_transport.disconnect().get();
|
||||
|
@ -97,7 +135,8 @@ TEST(websocket_transport_send, send_creates_and_sends_websocket_messages)
|
|||
return pplx::task_from_result();
|
||||
});
|
||||
|
||||
websocket_transport ws_transport(client);
|
||||
websocket_transport ws_transport{ client, connection_impl::create(_XPLATSTR("http://fake.uri"),
|
||||
_XPLATSTR(""), trace_level::none, std::make_shared<trace_log_writer>()) };
|
||||
|
||||
ws_transport.send(_XPLATSTR("ABC")).wait();
|
||||
|
||||
|
@ -116,7 +155,8 @@ TEST(websocket_transport_disconnect, disconnect_closes_websocket)
|
|||
return pplx::task_from_result();
|
||||
});
|
||||
|
||||
websocket_transport ws_transport(client);
|
||||
websocket_transport ws_transport{ client, connection_impl::create(_XPLATSTR("http://fake.uri"),
|
||||
_XPLATSTR(""), trace_level::none, std::make_shared<trace_log_writer>()) };
|
||||
|
||||
ws_transport.disconnect().get();
|
||||
|
||||
|
@ -132,10 +172,42 @@ TEST(websocket_transport_disconnect, disconnect_does_not_throw)
|
|||
return pplx::task_from_exception<void>(std::exception());
|
||||
});
|
||||
|
||||
websocket_transport ws_transport(client);
|
||||
websocket_transport ws_transport{ client, connection_impl::create(_XPLATSTR("http://fake.uri"),
|
||||
_XPLATSTR(""), trace_level::none, std::make_shared<trace_log_writer>()) };
|
||||
ws_transport.disconnect().get();
|
||||
}
|
||||
|
||||
TEST(websocket_transport_disconnect, disconnect_logs_exceptions)
|
||||
{
|
||||
auto client = std::make_shared<test_websocket_client>();
|
||||
client->set_close_function([]()->pplx::task<void>
|
||||
{
|
||||
return pplx::task_from_exception<void>(web_sockets::client::websocket_exception(_XPLATSTR("connection closing failed")));
|
||||
});
|
||||
|
||||
std::shared_ptr<log_writer> writer(std::make_shared<memory_log_writer>());
|
||||
|
||||
websocket_transport ws_transport{client, connection_impl::create(_XPLATSTR("http://fake.uri"),
|
||||
_XPLATSTR(""), trace_level::messages, writer)};
|
||||
|
||||
try
|
||||
{
|
||||
ws_transport.disconnect().get();
|
||||
}
|
||||
catch (...)
|
||||
{}
|
||||
|
||||
auto log_entries = std::dynamic_pointer_cast<memory_log_writer>(writer)->get_log_entries();
|
||||
|
||||
ASSERT_FALSE(log_entries.empty());
|
||||
|
||||
auto entry = remove_date_from_log_entry(log_entries[0]);
|
||||
|
||||
ASSERT_EQ(
|
||||
_XPLATSTR("[websocket transport] exception when closing websocket: connection closing failed\n"),
|
||||
entry);
|
||||
}
|
||||
|
||||
TEST(websocket_transport_disconnect, receive_not_called_after_disconnect)
|
||||
{
|
||||
auto client = std::make_shared<test_websocket_client>();
|
||||
|
@ -157,7 +229,8 @@ TEST(websocket_transport_disconnect, receive_not_called_after_disconnect)
|
|||
return pplx::create_task(receive_task_tce);
|
||||
});
|
||||
|
||||
websocket_transport ws_transport(client);
|
||||
websocket_transport ws_transport{ client, connection_impl::create(_XPLATSTR("http://fake.uri"),
|
||||
_XPLATSTR(""), trace_level::none, std::make_shared<trace_log_writer>()) };
|
||||
|
||||
ws_transport.connect(_XPLATSTR("http://fakeuri.org")).get();
|
||||
ws_transport.disconnect().get();
|
||||
|
@ -168,3 +241,62 @@ TEST(websocket_transport_disconnect, receive_not_called_after_disconnect)
|
|||
|
||||
ASSERT_EQ(2, num_called);
|
||||
}
|
||||
|
||||
template<class T>
|
||||
void receive_loop_logs_exception_runner(const T& e, const utility::string_t& expected_message);
|
||||
|
||||
TEST(websocket_transport_receive_loop, receive_loop_logs_websocket_exceptions)
|
||||
{
|
||||
receive_loop_logs_exception_runner(
|
||||
web_sockets::client::websocket_exception(_XPLATSTR("receive failed")),
|
||||
_XPLATSTR("[websocket transport] websocket exception when receiving data: receive failed\n"));
|
||||
}
|
||||
|
||||
TEST(websocket_transport_receive_loop, receive_loop_logs_if_receive_task_cancelled)
|
||||
{
|
||||
receive_loop_logs_exception_runner(
|
||||
pplx::task_canceled("cancelled"),
|
||||
_XPLATSTR("[websocket transport] receive task cancelled: cancelled\n"));
|
||||
}
|
||||
|
||||
TEST(websocket_transport_receive_loop, receive_loop_logs_std_exception)
|
||||
{
|
||||
receive_loop_logs_exception_runner(
|
||||
std::exception("exception"),
|
||||
_XPLATSTR("[websocket transport] error receiving response from websocket: exception\n"));
|
||||
}
|
||||
|
||||
template<class T>
|
||||
void receive_loop_logs_exception_runner(const T& e, const utility::string_t& expected_message)
|
||||
{
|
||||
pplx::event receive_event;
|
||||
auto client = std::make_shared<test_websocket_client>();
|
||||
|
||||
client->set_receive_function([&receive_event, &e]()->pplx::task<std::string>
|
||||
{
|
||||
receive_event.set();
|
||||
return pplx::task_from_exception<std::string>(e);
|
||||
});
|
||||
|
||||
std::shared_ptr<log_writer> writer(std::make_shared<memory_log_writer>());
|
||||
|
||||
websocket_transport ws_transport{ client, connection_impl::create(_XPLATSTR("http://fake.uri"),
|
||||
_XPLATSTR(""), trace_level::messages, writer) };
|
||||
|
||||
ws_transport.connect(_XPLATSTR("url"))
|
||||
.then([&receive_event]()
|
||||
{
|
||||
receive_event.wait();
|
||||
}).get();
|
||||
|
||||
// this is race'y but there is nothing we can block on
|
||||
pplx::wait(10);
|
||||
|
||||
auto log_entries = std::dynamic_pointer_cast<memory_log_writer>(writer)->get_log_entries();
|
||||
|
||||
ASSERT_FALSE(log_entries.empty());
|
||||
|
||||
auto entry = remove_date_from_log_entry(log_entries[0]);
|
||||
|
||||
ASSERT_EQ(expected_message, entry);
|
||||
}
|
Загрузка…
Ссылка в новой задаче