/* -*- 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 "HttpRequest.hpp" #include "Util.hpp" #include "lokassert.hpp" #include #include #include #include #include #include #include #include /// Test slow saving/uploading. /// We modify the document, save, and immediately /// modify again followed by closing the connection. /// In this scenario, it's not just that the document /// is modified at the time of unloading, which is /// covered by the UnitWOPIAsncUpload_ModifyClose /// test. Instead, here we close the connection /// while the document is being saved and uploaded. /// Unlike the failed upload scenario, this one /// will hit "upload in progress" and will test /// that in such a case we don't drop the latest /// changes, which were done while save/upload /// were in progress. /// Modify, Save, Modify, Close -> No data loss. class UnitWOPISlow : public WopiTestServer { STATE_ENUM(Phase, Load, WaitLoadStatus, WaitModifiedStatus, WaitPutFile) _phase; static constexpr auto LargeDocumentFilename = "large-six-hundred.odt"; /// The number of key input sent. std::size_t _inputCount; public: UnitWOPISlow() : WopiTestServer("UnitWOPISlow", LargeDocumentFilename) , _phase(Phase::Load) , _inputCount(0) { // We need more time than the default. setTimeout(std::chrono::minutes(10)); } std::unique_ptr assertPutFileRequest(const Poco::Net::HTTPRequest& request) override { LOG_TST("PutFile"); LOK_ASSERT_MESSAGE("Expected to be in Phase::WaitPutFile", _phase == Phase::WaitPutFile); // Triggered while closing. LOK_ASSERT_EQUAL(std::string("false"), request.get("X-COOL-WOPI-IsAutosave")); LOK_ASSERT_EQUAL(std::string("true"), request.get("X-COOL-WOPI-IsModifiedByUser")); passTest("Document uploaded on closing as expected."); return nullptr; } void onDocBrokerDestroy(const std::string& docKey) override { passTest("Document [" + docKey + "] uploaded and closed cleanly."); } /// The document is loaded. bool onDocumentLoaded(const std::string& message) override { LOG_TST("Doc (" << toString(_phase) << "): [" << message << ']'); LOK_ASSERT_MESSAGE("Expected to be in Phase::WaitLoadStatus", _phase == Phase::WaitLoadStatus); // Modify and wait for the notification. TRANSITION_STATE(_phase, Phase::WaitModifiedStatus); LOG_TST("Sending key input #" << ++_inputCount); WSD_CMD("key type=input char=97 key=0"); WSD_CMD("key type=up char=0 key=512"); return true; } /// The document is modified. Save, modify, and close it. bool onDocumentModified(const std::string& message) override { // We modify the document multiple times. // Only the first time is handled here. if (_phase == Phase::WaitModifiedStatus) { LOG_TST("Doc (" << toString(_phase) << "): [" << message << ']'); LOK_ASSERT_MESSAGE("Expected to be in Phase::WaitModifiedStatus", _phase == Phase::WaitModifiedStatus); // Save and immediately modify, then close the connection. WSD_CMD("save dontTerminateEdit=0 dontSaveIfUnmodified=0 " "extendedData=CustomFlag%3DCustom%20Value%3BAnotherFlag%3DAnotherValue"); LOG_TST("Sending key input #" << ++_inputCount); WSD_CMD("key type=input char=97 key=0"); WSD_CMD("key type=up char=0 key=512"); LOG_TST("Closing the connection."); deleteSocketAt(0); // Don't transition to WaitPutFile until after closing the socket. TRANSITION_STATE(_phase, Phase::WaitPutFile); } return true; } void invokeWSDTest() override { switch (_phase) { case Phase::Load: { TRANSITION_STATE(_phase, Phase::WaitLoadStatus); LOG_TST("Load: initWebsocket."); initWebsocket("/wopi/files/large-six-hundred.odt?access_token=anything"); WSD_CMD("load url=" + getWopiSrc()); break; } case Phase::WaitLoadStatus: break; case Phase::WaitModifiedStatus: break; case Phase::WaitPutFile: break; } } }; /// Test superfluous saves. class UnitSuperfluousSaves : public WopiTestServer { STATE_ENUM(Phase, Load, WaitLoadStatus, WaitModifiedStatus, WaitPutFile, Done) _phase; Util::Stopwatch _stopwatch; /// The number of key input sent. std::size_t _saveCount; int _uploadCount; //< The number of times we uploaded. public: UnitSuperfluousSaves() : WopiTestServer("UnitSuperfluousSaves") , _phase(Phase::Load) , _saveCount(0) , _uploadCount(0) { // We need more time than the default. setTimeout(std::chrono::minutes(2)); } std::unique_ptr assertPutFileRequest(const Poco::Net::HTTPRequest& request) override { ++_uploadCount; LOG_TST("PutFile #" << _uploadCount); LOK_ASSERT_EQUAL(std::string("false"), request.get("X-COOL-WOPI-IsAutosave")); LOK_ASSERT_EQUAL(std::string("false"), request.get("X-COOL-WOPI-IsExitSave")); if (_phase == Phase::WaitPutFile) { LOK_ASSERT_EQUAL(std::string("true"), request.get("X-COOL-WOPI-IsModifiedByUser")); LOK_ASSERT_EQUAL_MESSAGE("Expected to be in Phase::WaitPutFile", 1, _uploadCount); TRANSITION_STATE(_phase, Phase::Done); } else { LOK_ASSERT_EQUAL(std::string("false"), request.get("X-COOL-WOPI-IsModifiedByUser")); LOK_ASSERT_MESSAGE("Expected to be in Phase::WaitPutFile", _phase == Phase::Done); // LOK_ASSERT_EQUAL_MESSAGE("Expected to be in Phase::WaitPutFile", 2, _uploadCount); } return nullptr; } /// The document is loaded. bool onDocumentLoaded(const std::string& message) override { LOG_TST("Doc (" << toString(_phase) << "): [" << message << ']'); LOK_ASSERT_MESSAGE("Expected to be in Phase::WaitLoadStatus", _phase == Phase::WaitLoadStatus); // Modify and wait for the notification. TRANSITION_STATE(_phase, Phase::WaitModifiedStatus); WSD_CMD("key type=input char=97 key=0"); WSD_CMD("key type=up char=0 key=512"); return true; } /// The document is modified. Save, modify, and close it. bool onDocumentModified(const std::string& message) override { LOG_TST("Doc (" << toString(_phase) << "): [" << message << ']'); LOK_ASSERT_MESSAGE("Expected to be in Phase::WaitModifiedStatus", _phase == Phase::WaitModifiedStatus); _stopwatch.restart(); // Don't transition to WaitPutFile until after closing the socket. TRANSITION_STATE(_phase, Phase::WaitPutFile); return true; } void invokeWSDTest() override { switch (_phase) { case Phase::Load: { TRANSITION_STATE(_phase, Phase::WaitLoadStatus); LOG_TST("Load: initWebsocket."); initWebsocket("/wopi/files/" + getTestname() + "?access_token=anything"); WSD_CMD("load url=" + getWopiSrc()); break; } case Phase::WaitLoadStatus: break; case Phase::WaitModifiedStatus: break; case Phase::WaitPutFile: { // Save while we're waiting. LOG_TST("Sending key input #" << _saveCount); WSD_CMD("save dontTerminateEdit=0 dontSaveIfUnmodified=0"); } break; case Phase::Done: { if (_stopwatch.elapsed(std::chrono::minutes(1))) { passTest("No unexpected conditions met"); } } break; } } }; UnitBase** unit_create_wsd_multi(void) { return new UnitBase* [3] { new UnitWOPISlow(), new UnitSuperfluousSaves(), nullptr }; } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */