/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */ /* * Copyright the Collabora Online contributors. * * SPDX-License-Identifier: MPL-2.0 * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace helpers; /// Tests the HTTP WebSocket API of coolwsd. The server has to be started manually before running this test. class HTTPCrashTest : public CPPUNIT_NS::TestFixture { const Poco::URI _uri; Poco::Net::HTTPResponse _response; std::shared_ptr _socketPoll; CPPUNIT_TEST_SUITE(HTTPCrashTest); CPPUNIT_TEST(testBarren); CPPUNIT_TEST(testCrashKit); CPPUNIT_TEST(testRecoverAfterKitCrash); CPPUNIT_TEST(testCrashForkit); CPPUNIT_TEST_SUITE_END(); void testBarren(); void testCrashKit(); void testRecoverAfterKitCrash(); void testCrashForkit(); void killDocKitProcesses(const std::string& testname); public: HTTPCrashTest() : _uri(helpers::getTestServerURI()) , _socketPoll(std::make_shared("HttpCrashPoll")) { #if ENABLE_SSL Poco::Net::initializeSSL(); // Just accept the certificate anyway for testing purposes Poco::SharedPtr invalidCertHandler = new Poco::Net::AcceptCertificateHandler(false); Poco::Net::Context::Params sslParams; Poco::Net::Context::Ptr sslContext = new Poco::Net::Context(Poco::Net::Context::CLIENT_USE, sslParams); Poco::Net::SSLManager::instance().initializeClient(nullptr, std::move(invalidCertHandler), std::move(sslContext)); #endif } ~HTTPCrashTest() { #if ENABLE_SSL Poco::Net::uninitializeSSL(); #endif } void setUp() { resetTestStartTime(); waitForKitPidsReady("setUp"); resetTestStartTime(); _socketPoll->startThread(); } void tearDown() { _socketPoll->joinThread(); resetTestStartTime(); waitForKitPidsReady("tearDown"); resetTestStartTime(); } }; void HTTPCrashTest::testBarren() { // Kill all kit processes and try loading a document. const char* testname = "barren "; try { TST_LOG("Killing all kits"); helpers::killAllKitProcesses(testname); // Do not wait for spare kit to start up here TST_LOG("Loading after kill."); // Load a document and get its status. std::shared_ptr socket = loadDocAndGetSession(_socketPoll, "hello.odt", _uri, testname); sendTextFrame(socket, "status", testname); assertResponseString(socket, "status:", testname); socket->asyncShutdown(); LOK_ASSERT_MESSAGE("Expected successful disconnection of the WebSocket", socket->waitForDisconnection(std::chrono::seconds(5))); } catch (const Poco::Exception& exc) { LOK_ASSERT_FAIL(exc.displayText()); } } void HTTPCrashTest::testCrashKit() { const char* testname = "crashKit "; try { TST_LOG("Loading document"); std::shared_ptr socket = loadDocAndGetSession(_socketPoll, "empty.odt", _uri, testname); TST_LOG("Allowing time for kit to connect to wsd to get cleanly killed"); std::this_thread::sleep_for(std::chrono::seconds(1)); TST_LOG("Killing coolkit instances."); helpers::killAllKitProcesses(testname); // TST_LOG("Reading the error code from the socket."); //FIXME: implement in WebSocketSession. // std::string message; // const int statusCode = getErrorCode(socket, message, testname); // LOK_ASSERT_EQUAL(static_cast(Poco::Net::WebSocket::WS_ENDPOINT_GOING_AWAY), statusCode); // respond close frame TST_LOG("Shutting down socket."); socket->asyncShutdown(); LOK_ASSERT_MESSAGE("Expected successful disconnection of the WebSocket", socket->waitForDisconnection(std::chrono::seconds(5))); } catch (const Poco::Exception& exc) { LOK_ASSERT_FAIL(exc.displayText()); } } void HTTPCrashTest::testRecoverAfterKitCrash() { const char* testname = "recoverAfterKitCrash "; try { TST_LOG("Loading document"); std::shared_ptr socket1 = loadDocAndGetSession(_socketPoll, "empty.odt", _uri, testname); TST_LOG("Allowing time for kit to connect to wsd to get cleanly killed"); std::this_thread::sleep_for(std::chrono::seconds(1)); TST_LOG("Killing coolkit instances."); killDocKitProcesses(testname); TST_LOG("Reconnect after kill."); std::shared_ptr socket2 = loadDocAndGetSession( _socketPoll, "empty.odt", _uri, testname, /*isView=*/true, /*isAssert=*/false); sendTextFrame(socket2, "status", testname); assertResponseString(socket2, "status:", testname); socket2->asyncShutdown(); socket1->asyncShutdown(); LOK_ASSERT_MESSAGE("Expected successful disconnection of the WebSocket 2", socket2->waitForDisconnection(std::chrono::seconds(5))); LOK_ASSERT_MESSAGE("Expected successful disconnection of the WebSocket 1", socket1->waitForDisconnection(std::chrono::seconds(5))); } catch (const Poco::Exception& exc) { LOK_ASSERT_FAIL(exc.displayText()); } } void HTTPCrashTest::testCrashForkit() { const char* testname = "crashForkit "; try { TST_LOG("Loading document"); std::shared_ptr socket = loadDocAndGetSession(_socketPoll, "empty.odt", _uri, testname); TST_LOG("Allowing time for kit to connect to wsd to get cleanly killed"); std::this_thread::sleep_for(std::chrono::seconds(1)); TST_LOG("Killing forkit."); helpers::killPid(testname, getForKitPid()); TST_LOG("Communicating after kill."); sendTextFrame(socket, "status", testname); assertResponseString(socket, "status:", testname); // respond close frame socket->asyncShutdown(); LOK_ASSERT_MESSAGE("Expected successful disconnection of the WebSocket", socket->waitForDisconnection(std::chrono::seconds(5))); TST_LOG("Killing coolkit."); helpers::killAllKitProcesses(testname); // Forkit should restart waitForKitPidsReady(testname); TST_LOG("Communicating after kill."); socket = loadDocAndGetSession(_socketPoll, "empty.odt", _uri, testname); sendTextFrame(socket, "status", testname); assertResponseString(socket, "status:", testname); socket->asyncShutdown(); LOK_ASSERT_MESSAGE("Expected successful disconnection of the WebSocket", socket->waitForDisconnection(std::chrono::seconds(5))); } catch (const Poco::Exception& exc) { LOK_ASSERT_FAIL(exc.displayText()); } } void HTTPCrashTest::killDocKitProcesses(const std::string& testname) { for (pid_t pid : getDocKitPids()) { helpers::killPid(testname, pid); } waitForKitPidsReady(testname); } CPPUNIT_TEST_SUITE_REGISTRATION(HTTPCrashTest); /* vim:set shiftwidth=4 softtabstop=4 expandtab: */