// SPDX-FileCopyrightText: 2021 Daniel Vrátil // // SPDX-License-Identifier: MIT #include "testhttpserver.h" #include "testobject.h" #include "qcoroiodevice_macros.h" #include "testloop.h" #include "qcoro/network/qcorolocalsocket.h" #include #include #include static const QByteArray blockRequest = "GET /block HTTP/1.1\r\n"; static const QByteArray streamRequest = "GET /stream HTTP/1.1\r\n"; class QCoroLocalSocketTest : public QCoro::TestObject { Q_OBJECT private: QCoro::Task<> testWaitForConnectedTriggers_coro(QCoro::TestContext) { QLocalSocket socket; QCORO_DELAY(socket.connectToServer(QCoroLocalSocketTest::getSocketName())); co_await qCoro(socket).waitForConnected(); QCORO_COMPARE(socket.state(), QLocalSocket::ConnectedState); QCORO_VERIFY(mServer.waitForConnection()); } void testThenWaitForConnectedTriggers_coro(TestLoop &el) { QLocalSocket socket; QCORO_DELAY(socket.connectToServer(QCoroLocalSocketTest::getSocketName())); bool called = false; qCoro(socket).waitForConnected().then([&](bool connected) { called = true; el.quit(); QVERIFY(connected); }); el.exec(); QVERIFY(called); QVERIFY(mServer.waitForConnection()); } QCoro::Task<> testWaitForDisconnectedTriggers_coro(QCoro::TestContext) { QLocalSocket socket; socket.connectToServer(QCoroLocalSocketTest::getSocketName()); QCORO_COMPARE(socket.state(), QLocalSocket::ConnectedState); QCORO_DELAY(socket.disconnectFromServer()); co_await qCoro(socket).waitForDisconnected(); QCORO_COMPARE(socket.state(), QLocalSocket::UnconnectedState); QCORO_VERIFY(mServer.waitForConnection()); } void testThenWaitForDisconnectedTriggers_coro(TestLoop &el) { QLocalSocket socket; socket.connectToServer(QCoroLocalSocketTest::getSocketName()); QCOMPARE(socket.state(), QLocalSocket::ConnectedState); QCORO_DELAY(socket.disconnectFromServer()); bool called = false; qCoro(socket).waitForDisconnected().then([&](bool disconnected) { called = true; el.quit(); QVERIFY(disconnected); }); el.exec(); QVERIFY(called); QVERIFY(mServer.waitForConnection()); } // On Linux at least, QLocalSocket connects immediately and synchronously QCoro::Task<> testDoesntCoAwaitConnectedSocket_coro(QCoro::TestContext context) { context.setShouldNotSuspend(); QLocalSocket socket; socket.connectToServer(QCoroLocalSocketTest::getSocketName()); QCORO_COMPARE(socket.state(), QLocalSocket::ConnectedState); co_await qCoro(socket).waitForConnected(); QCORO_VERIFY(mServer.waitForConnection()); } void testThenDoesntCoAwaitConnectedSocket_coro(TestLoop &el) { QLocalSocket socket; socket.connectToServer(QCoroLocalSocketTest::getSocketName()); QCOMPARE(socket.state(), QLocalSocket::ConnectedState); bool called = false; qCoro(socket).waitForConnected().then([&](bool connected) { called = true; el.quit(); QVERIFY(connected); }); el.exec(); QVERIFY(called); QVERIFY(mServer.waitForConnection()); } QCoro::Task<> testDoesntCoAwaitDisconnectedSocket_coro(QCoro::TestContext context) { context.setShouldNotSuspend(); mServer.setExpectTimeout(true); QLocalSocket socket; QCORO_COMPARE(socket.state(), QLocalSocket::UnconnectedState); co_await qCoro(socket).waitForDisconnected(); } void testThenDoesntCoAwaitDisconnectedSocket_coro(TestLoop &el) { mServer.setExpectTimeout(true); QLocalSocket socket; QCOMPARE(socket.state(), QLocalSocket::UnconnectedState); bool called = false; qCoro(socket).waitForDisconnected().then([&](bool disconnected) { called = true; el.quit(); QVERIFY(!disconnected); }); el.exec(); QVERIFY(called); } QCoro::Task<> testConnectToServerWithArgs_coro(QCoro::TestContext context) { context.setShouldNotSuspend(); QLocalSocket socket; co_await qCoro(socket).connectToServer(QCoroLocalSocketTest::getSocketName()); QCORO_COMPARE(socket.state(), QLocalSocket::ConnectedState); QCORO_VERIFY(mServer.waitForConnection()); } void testThenConnectToServerWithArgs_coro(TestLoop &el) { QLocalSocket socket; bool called = false; qCoro(socket).connectToServer(QCoroLocalSocketTest::getSocketName()).then([&](bool connected) { called = true; el.quit(); QVERIFY(connected); }); el.exec(); QVERIFY(called); QVERIFY(mServer.waitForConnection()); } QCoro::Task<> testConnectToServer_coro(QCoro::TestContext context) { context.setShouldNotSuspend(); QLocalSocket socket; socket.setServerName(QCoroLocalSocketTest::getSocketName()); co_await qCoro(socket).connectToServer(); QCORO_COMPARE(socket.state(), QLocalSocket::ConnectedState); QCORO_VERIFY(mServer.waitForConnection()); } void testThenConnectToServer_coro(TestLoop &el) { QLocalSocket socket; socket.setServerName(QCoroLocalSocketTest::getSocketName()); bool called = false; qCoro(socket).connectToServer().then([&](bool connected) { called = true; el.quit(); QVERIFY(connected); }); el.exec(); QVERIFY(called); QVERIFY(mServer.waitForConnection()); } QCoro::Task<> testWaitForConnectedTimeout_coro(QCoro::TestContext) { mServer.setExpectTimeout(true); QLocalSocket socket; QCORO_TEST_TIMEOUT(co_await qCoro(socket).waitForConnected(10ms)); } void testThenWaitForConnectedTimeout_coro(TestLoop &el) { mServer.setExpectTimeout(true); QLocalSocket socket; bool called = false; qCoro(socket).waitForConnected(10ms).then([&](bool connected) { called = true; el.quit(); QVERIFY(!connected); }); const auto start = std::chrono::steady_clock::now(); el.exec(); const auto end = std::chrono::steady_clock::now(); QVERIFY(end - start < 500ms); QVERIFY(called); } QCoro::Task<> testWaitForDisconnectedTimeout_coro(QCoro::TestContext) { QLocalSocket socket; socket.connectToServer(QCoroLocalSocketTest::getSocketName()); QCORO_COMPARE(socket.state(), QLocalSocket::ConnectedState); QCORO_TEST_TIMEOUT(co_await qCoro(socket).waitForDisconnected(10ms)); QCORO_VERIFY(mServer.waitForConnection()); } void testThenWaitForDisconnectedTimeout_coro(TestLoop &el) { QLocalSocket socket; socket.connectToServer(QCoroLocalSocketTest::getSocketName()); QCOMPARE(socket.state(), QLocalSocket::ConnectedState); bool called = false; qCoro(socket).waitForDisconnected(10ms).then([&](bool disconnected) { called = true; el.quit(); QVERIFY(!disconnected); }); const auto start = std::chrono::steady_clock::now(); el.exec(); const auto end = std::chrono::steady_clock::now(); QVERIFY(end - start < 500ms); QVERIFY(called); QVERIFY(mServer.waitForConnection()); } QCoro::Task<> testReadAllTriggers_coro(QCoro::TestContext) { QLocalSocket socket; socket.connectToServer(QCoroLocalSocketTest::getSocketName()); QCORO_COMPARE(socket.state(), QLocalSocket::ConnectedState); const auto written = co_await qCoro(socket).write(streamRequest); QCORO_COMPARE(written, streamRequest.size()); QCORO_TEST_IODEVICE_READALL(socket); QCORO_VERIFY(mServer.waitForConnection()); } void testThenReadAllTriggers_coro(TestLoop &el) { QLocalSocket socket; socket.connectToServer(QCoroLocalSocketTest::getSocketName()); QCOMPARE(socket.state(), QLocalSocket::ConnectedState); bool called = false; qCoro(socket).readAll().then([&](const QByteArray &data) { called = true; el.quit(); QVERIFY(!data.isEmpty()); }); qCoro(socket).write(blockRequest).then([&](qint64 written) { QCOMPARE(written, blockRequest.size()); }); el.exec(); QVERIFY(called); QVERIFY(mServer.waitForConnection()); } QCoro::Task<> testReadTriggers_coro(QCoro::TestContext) { QLocalSocket socket; socket.connectToServer(QCoroLocalSocketTest::getSocketName()); QCORO_COMPARE(socket.state(), QLocalSocket::ConnectedState); const auto written = co_await qCoro(socket).write(streamRequest); QCORO_COMPARE(written, streamRequest.size()); QCORO_TEST_IODEVICE_READ(socket); QCORO_VERIFY(mServer.waitForConnection()); } void testThenReadTriggers_coro(TestLoop &el) { QLocalSocket socket; socket.connectToServer(QCoroLocalSocketTest::getSocketName()); QCOMPARE(socket.state(), QLocalSocket::ConnectedState); bool called = false; qCoro(socket).read(1).then([&](const QByteArray &data) { called = true; el.quit(); QCOMPARE(data.size(), 1); }); qCoro(socket).write(blockRequest).then([&](qint64 written) { QCOMPARE(written, blockRequest.size()); }); el.exec(); QVERIFY(called); QVERIFY(mServer.waitForConnection()); } QCoro::Task<> testReadLineTriggers_coro(QCoro::TestContext) { QLocalSocket socket; socket.connectToServer(QCoroLocalSocketTest::getSocketName()); QCORO_COMPARE(socket.state(), QLocalSocket::ConnectedState); const auto written = co_await qCoro(socket).write(streamRequest); QCORO_COMPARE(written, streamRequest.size()); QCORO_TEST_IODEVICE_READLINE(socket); QCORO_COMPARE(lines.size(), 14); QCORO_VERIFY(mServer.waitForConnection()); } void testThenReadLineTriggers_coro(TestLoop &el) { QLocalSocket socket; socket.connectToServer(QCoroLocalSocketTest::getSocketName()); QCOMPARE(socket.state(), QLocalSocket::ConnectedState); bool called = false; qCoro(socket).readLine().then([&](const QByteArray &data) { called = true; el.quit(); QVERIFY(!data.isEmpty()); }); qCoro(socket).write(blockRequest).then([&](qint64 written) { QCOMPARE(written, blockRequest.size()); }); el.exec(); QVERIFY(called); QVERIFY(mServer.waitForConnection()); } private Q_SLOTS: void init() { mServer.start(QCoroLocalSocketTest::getSocketName()); } void cleanup() { mServer.stop(); } addCoroAndThenTests(WaitForConnectedTriggers) addCoroAndThenTests(WaitForConnectedTimeout) addCoroAndThenTests(WaitForDisconnectedTriggers) addCoroAndThenTests(WaitForDisconnectedTimeout) addCoroAndThenTests(DoesntCoAwaitConnectedSocket) addCoroAndThenTests(DoesntCoAwaitDisconnectedSocket) addCoroAndThenTests(ConnectToServerWithArgs) addCoroAndThenTests(ConnectToServer) addCoroAndThenTests(ReadAllTriggers) addCoroAndThenTests(ReadTriggers) addCoroAndThenTests(ReadLineTriggers) private: static QString getSocketName() { return QStringLiteral("%1-%2") .arg(QCoreApplication::applicationName()) .arg(QCoreApplication::applicationPid()); } TestHttpServer mServer; }; QTEST_GUILESS_MAIN(QCoroLocalSocketTest) #include "qcorolocalsocket.moc"