/* -*- 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 /// Load torture testcase. class UnitLoadTorture : public UnitWSD { void loadTorture(const std::string& name, const std::string& docName, const size_t thread_count, const size_t max_jitter_ms = 100); TestResult testLoadTortureODT(); TestResult testLoadTortureODS(); TestResult testLoadTortureODP(); TestResult testLoadTortureODG(); TestResult testLoadTorture(); public: UnitLoadTorture(); void invokeWSDTest() override; }; void UnitLoadTorture::loadTorture(const std::string& name, const std::string& docName, const size_t thread_count, const size_t max_jitter_ms) { // Load same document from many threads together. std::string documentPath, documentURL; helpers::getDocumentPathAndURL(docName, documentPath, documentURL, name); TST_LOG("Starting test on " << documentURL << ' ' << documentPath); std::mutex stateLock; std::vector view_ids; std::atomic sum_view_ids; sum_view_ids = 0; std::atomic num_of_views(0); std::atomic num_to_load(thread_count); std::shared_ptr poll = std::make_shared("WebSocketPoll"); poll->startThread(); std::vector threads; for (size_t i = 0; i < thread_count; ++i) { threads.emplace_back([&] { std::ostringstream oss; oss << std::hex << std::this_thread::get_id(); const std::string id = oss.str(); TST_LOG(": #" << id << ", views: " << num_of_views << ", to load: " << num_to_load); try { // Load a document and wait for the status. auto wsSession = http::WebSocketSession::create(helpers::getTestServerURI()); http::Request req(documentURL); wsSession->asyncRequest(req, poll); wsSession->sendMessage("load url=" + documentURL); // 20s is double of the default. std::vector message = wsSession->waitForMessage("status:", std::chrono::seconds(20), name + id + ' '); const std::string status = COOLProtocol::getFirstLine(message); int viewid = -1; LOK_ASSERT(COOLProtocol::getTokenIntegerFromMessage(status, "viewid", viewid)); LOK_ASSERT("Failed to create view in time " && viewid >= 0); { std::lock_guard guard(stateLock); LOK_ASSERT("Duplicate view-id generated " && std::find(view_ids.begin(), view_ids.end(), viewid) == view_ids.end()); view_ids.push_back(viewid); } ++num_of_views; --num_to_load; TST_LOG(": #" << id << ", new viewId: " << viewid << ", loaded views: " << num_of_views << ", to load: " << num_to_load); // Get all the views loaded - lean on test timeout to interrupt. while (num_to_load > 0) std::this_thread::sleep_for(std::chrono::milliseconds(20)); // delay randomly to close in random order: const auto ms = (max_jitter_ms > 0 ? std::chrono::milliseconds(Util::rng::getNext() % max_jitter_ms) : std::chrono::milliseconds(0)); std::this_thread::sleep_for(ms); --num_of_views; TST_LOG(": #" << id << ", view: " << num_of_views << " unloading"); } catch (const std::exception& exc) { TST_LOG(": #" << id << ", Exception: " << exc.what()); --num_to_load; } }); } for (auto& thread : threads) { try { thread.join(); } catch (const std::exception& exc) { TST_LOG(": Exception: " << exc.what()); } } } UnitBase::TestResult UnitLoadTorture::testLoadTortureODT() { const int thread_count = 6; loadTorture(testname, "empty.odt", thread_count); return TestResult::Ok; } UnitBase::TestResult UnitLoadTorture::testLoadTortureODS() { const int thread_count = 6; loadTorture(testname, "empty.ods", thread_count); return TestResult::Ok; } UnitBase::TestResult UnitLoadTorture::testLoadTortureODP() { const int thread_count = 6; loadTorture(testname, "empty.odp", thread_count); return TestResult::Ok; } UnitBase::TestResult UnitLoadTorture::testLoadTortureODG() { const int thread_count = 6; loadTorture(testname, "empty.odg", thread_count); return TestResult::Ok; } UnitBase::TestResult UnitLoadTorture::testLoadTorture() { const int thread_count = 3; const int max_jitter_ms = 75; std::vector docNames = { "setclientpart.ods", "hello.odt", "viewcursor.odp" }; std::vector threads; threads.reserve(docNames.size()); for (const auto& docName : docNames) { threads.emplace_back([&] { const auto name = "loadTorture_" + docName + ' '; loadTorture(name, docName, thread_count, max_jitter_ms); }); } for (auto& thread : threads) { thread.join(); } return TestResult::Ok; } UnitLoadTorture::UnitLoadTorture() : UnitWSD("UnitLoadTorture") { // Double of the default. constexpr std::chrono::minutes timeout_minutes(1); setTimeout(timeout_minutes); } void UnitLoadTorture::invokeWSDTest() { UnitBase::TestResult result = testLoadTortureODT(); if (result != TestResult::Ok) exitTest(result); result = testLoadTortureODS(); if (result != TestResult::Ok) exitTest(result); result = testLoadTortureODP(); if (result != TestResult::Ok) exitTest(result); result = testLoadTortureODG(); if (result != TestResult::Ok) exitTest(result); result = testLoadTorture(); exitTest(result); } UnitBase* unit_create_wsd(void) { return new UnitLoadTorture(); } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */