- Nie gryzie ci się kontekst boost:asio z QEventLoop Qt? — MarekR22
- ani trochę:P — Cyberah
- w zasadzie to jestem miło zaskoczony, że to działa, bo rzeczywiście parę osób odradzało mieszanie boost, wątków i Qt5, bo może być buba, ale na razie jest git, a sygnały wysyłane z handlerów mogą być elegancko przetworzone dalej. — Cyberah
Żeby było jasnet zarówno QEventLoop
jak boost::asion::io_context
robią to samo.
QEventLoop
wywoławane jest przez QApplication:exec()
i blokuje wątek, aż do nadejścia jakiegoś zdarzenia od systemu.
- tak samo
boost::asion::io_context::run
- blokuje wątek aż do nadejścia zdarzeń od systemu.
Ergo oba nie mogą działać na jednym wątku, bo jeden be∂zie blokować drugi.
Jeśli boost::asion::io_context::run
robisz na osobnym wątku, to jest szansa, że to działa. Niestety z moich obserwacji wynika, że większość developerów z medium level nie ogarnia wielowątkowości, a beginning wydaje się tylko, że umieją wielowątkowość.
Co do samego testu. To może być coś w tym stylu (pisane z palca).
Kopiuj
class IAsioSocket {
public:
virtual ~IAsioSocket() {}
virtual void async_connect(const endpoint_type & peer_endpoint, ConnectHandler && handler) = 0;
};
class INetworkDependencies {
public:
virtual ~INetworkDependencies() {}
virtual std::unique_ptr<IAsioSocket> makeSocket() = 0;
};
class Client {
public:
explicit Client(INetworkDependencies *dependencies)
: m_dependencies{dependencies}
, m_socket{m_dependencies->makeSocket()}
{}
void connect(asio::ip::address const& ip_address, unsigned short const port) {
asio::ip::tcp::endpoint ep{ ip_address, port };
m_socket->async_connect(ep,
[this](auto const& ec) {
on_connected(ec);
});
}
void on_connected(system::error_code const& ec) {
if (!ec)
emit connected();
else
emit badConnect(ec);
}
signal:
void connected();
void badConnect(system::error_code const& ec);
private:
INetworkDependencies *m_dependencies;
std::unique_ptr<IAsioSocket> m_socket;
};
Kopiuj
class MockAsioSocket : public IAsioSocket {
public:
MOCK_METHOD(void, async_connect, (const endpoint_type & peer_endpoint, ConnectHandler && handler), (override));
};
class MockNetworkDependencies : public INetworkDependencies {
public:
MOCK_METHOD(std::unique_ptr<IAsioSocket>, makeSocket, (), (override));
};
Kopiuj
class ClientTest : public testing::Test
{
public:
void SetUp() override
{
constructClient();
}
void constructClient()
{
EXPECT_CALL(mockAsio, makeSocket()).WillOnce(Invoke([this] {
auto socket = std::make_unique<MockAsioSocket>();
mockSocket = socket.get();
return socket;
}));
client = std::make_unique<Client>(&mockAsio);
ASSERT_TRUE(Mock::VerifyAndClearExpectations(&mockAsio));
errorSpy = std::make_unique<QSignalSpy>(client, SIGNAL(badConnect(const system::error_code&)));
successSpy = std::make_unique<QSignalSpy>(client, SIGNAL(connected()));
}
void checkNoSignals()
{
ASSERT_EQ(errorSpy.count(), 0);
ASSERT_EQ(successSpy.count(), 0);
}
void startConnecting()
{
EXPECT_CALL(*mockSocket, async_connect(_, _))
.WillOnce(SaveArg<1>(&connectHandler));
client->connect(TestAddress, TestPort);
ASSERT_TRUE(Mock::VerifyAndClearExpectations(mockSocket));
}
void successfullConnect()
{
ASSERT_TRUE(!!connectHandler);
ASSERT_EQ(errorSpy.count(), 0);
ASSERT_EQ(successSpy.count(), 0);
connectHandler(TestSuccessValue);
ASSERT_EQ(errorSpy.count(), 0);
ASSERT_EQ(successSpy.count(), 1);
}
MockNetworkDependencies mockAsio;
MockAsioSocket *mockSocket = nullptr;
std::unique_ptr<QSignalSpy> errorSpy;
std::unique_ptr<QSignalSpy> successSpy;
std::unique_ptr<Client> client;
ConnectHandler connectHandler;
};
class ClientConstructedTest : public TestClientTest
{};
TEST_F(ClientConstructedTest, connecInvokesBoostAsioAsyncConnect)
{
ASSERT_NO_FATAL_FAILURE(startConnecting());
}
class ConnectingClientTest : public TestClientTest
{
void SetUp() overide
{
TestClientTest::SetUp();
ASSERT_NO_FATAL_FAILURE(startConnecting());
}
};
TEST_F(ConnectingClientTest, whenAsioReportsErrorClientReportsError)
{
ASSERT_NO_FATAL_FAILURE(checkNoSignals());
connectHandler(TestErrorValue);
ASSERT_EQ(errorSpy.count(), 1);
ASSERT_EQ(successSpy.count(), 0);
}
TEST_F(ConnectingClientTest, whenAsioReportsSuccessClientEntersConnectedState)
{
ASSERT_NO_FATAL_FAILURE(checkNoSignals());
ASSERT_NO_FATAL_FAILURE(successfullConnect());
}
Swoją drogą, to że asio::ip::tcp::socket sock{ m_ioc }
masz jako zmienną lokalną, kiedy używasz asynchronicznego API to dość poważny błąd.
W moim kodzie jest to poprawione.
boost:asio
, kiedy dysponujesz Qt! Nie gryzie ci się kontekstboost:asio
zQEventLoop
Qt?alagner