/* -*- 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 namespace { void getCursor(const std::string& message, int& cursorX, int& cursorY, int& cursorWidth, int& cursorHeight, const std::string& testname) { Poco::JSON::Parser parser; const Poco::Dynamic::Var result = parser.parse(message); const auto& command = result.extract(); std::string text = command->get("commandName").toString(); LOK_ASSERT_EQUAL(std::string(".uno:CellCursor"), text); text = command->get("commandValues").toString(); LOK_ASSERT(!text.empty()); StringVector position(StringVector::tokenize(text, ',')); cursorX = std::stoi(position[0]); cursorY = std::stoi(position[1]); cursorWidth = std::stoi(position[2]); cursorHeight = std::stoi(position[3]); LOK_ASSERT(cursorX >= 0); LOK_ASSERT(cursorY >= 0); LOK_ASSERT(cursorWidth >= 0); LOK_ASSERT(cursorHeight >= 0); } void limitCursor( const std::function& socket, int cursorX, int cursorY, int cursorWidth, int cursorHeight, int docWidth, int docHeight)>& keyhandler, const std::function& checkhandler, const std::string& testname) { int docSheet = -1; int docSheets = 0; int docHeight = 0; int docWidth = 0; int docViewId = -1; int newSheet = -1; int newSheets = 0; int newHeight = 0; int newWidth = 0; int cursorX = 0; int cursorY = 0; int cursorWidth = 0; int cursorHeight = 0; std::string response; Poco::URI uri(helpers::getTestServerURI()); std::shared_ptr socketPoll = std::make_shared("CursorPoll"); socketPoll->startThread(); std::shared_ptr socket = helpers::loadDocAndGetSession(socketPoll, "empty.ods", uri, testname); // check document size helpers::sendTextFrame(socket, "status", testname); response = helpers::assertResponseString(socket, "status:", testname); helpers::parseDocSize(response.substr(7), "spreadsheet", docSheet, docSheets, docWidth, docHeight, docViewId, testname); // Send an arrow key to initialize the CellCursor, otherwise we get "EMPTY". helpers::sendTextFrame(socket, "key type=input char=0 key=1027", testname); std::string text; Poco::format(text, "commandvalues " "command=.uno:CellCursor?outputHeight=%d&outputWidth=%d&tileHeight=%d&tileWidth=%" "d", 256, 256, 3840, 3840); helpers::sendTextFrame(socket, text, testname); const auto cursor = helpers::getResponseString(socket, "commandvalues:", testname); getCursor(cursor.substr(14), cursorX, cursorY, cursorWidth, cursorHeight, testname); // move cursor keyhandler(socket, cursorX, cursorY, cursorWidth, cursorHeight, docWidth, docHeight); // filter messages, and expect to receive new document size response = helpers::assertResponseString(socket, "status:", testname); helpers::parseDocSize(response.substr(7), "spreadsheet", newSheet, newSheets, newWidth, newHeight, docViewId, testname); LOK_ASSERT_EQUAL(docSheets, newSheets); LOK_ASSERT_EQUAL(docSheet, newSheet); // check new document size checkhandler(docWidth, docHeight, newWidth, newHeight); } } /// Test suite for cursor handling. class UnitCursor : public UnitWSD { TestResult testMaxColumn(); TestResult testMaxRow(); TestResult testInsertAnnotationWriter(); TestResult testEditAnnotationWriter(); TestResult testInsertAnnotationCalc(); public: UnitCursor() : UnitWSD("UnitCursor") { } void invokeWSDTest() override; }; UnitBase::TestResult UnitCursor::testMaxColumn() { try { limitCursor( // move cursor to last column [&](const std::shared_ptr& socket, int cursorX, int cursorY, int cursorWidth, int cursorHeight, int docWidth, int docHeight) { LOK_ASSERT(cursorX >= 0); LOK_ASSERT(cursorY >= 0); LOK_ASSERT(cursorWidth >= 0); LOK_ASSERT(cursorHeight >= 0); LOK_ASSERT(docWidth >= 0); LOK_ASSERT(docHeight >= 0); const std::string text = "key type=input char=0 key=1027"; while (cursorX <= docWidth) { helpers::sendTextFrame(socket, text, testname); cursorX += cursorWidth; } }, // check new document width [&](int docWidth, int docHeight, int newWidth, int newHeight) { LOK_ASSERT_EQUAL(docHeight, newHeight); LOK_ASSERT(newWidth > docWidth); }, "maxColumn"); } catch (const Poco::Exception& exc) { LOK_ASSERT_FAIL(exc.displayText()); } return TestResult::Ok; } UnitBase::TestResult UnitCursor::testMaxRow() { try { limitCursor( // move cursor to last row [&](const std::shared_ptr& socket, int cursorX, int cursorY, int cursorWidth, int cursorHeight, int docWidth, int docHeight) { LOK_ASSERT(cursorX >= 0); LOK_ASSERT(cursorY >= 0); LOK_ASSERT(cursorWidth >= 0); LOK_ASSERT(cursorHeight >= 0); LOK_ASSERT(docWidth >= 0); LOK_ASSERT(docHeight >= 0); const std::string text = "key type=input char=0 key=1024"; while (cursorY <= docHeight) { helpers::sendTextFrame(socket, text, testname); cursorY += cursorHeight; } }, // check new document height [&](int docWidth, int docHeight, int newWidth, int newHeight) { LOK_ASSERT_EQUAL(docWidth, newWidth); LOK_ASSERT(newHeight > docHeight); }, "maxRow"); } catch (const Poco::Exception& exc) { LOK_ASSERT_FAIL(exc.displayText()); } return TestResult::Ok; } UnitBase::TestResult UnitCursor::testInsertAnnotationWriter() { std::string documentPath, documentURL; helpers::getDocumentPathAndURL("hello.odt", documentPath, documentURL, testname); Poco::URI uri(helpers::getTestServerURI()); std::shared_ptr socketPoll = std::make_shared("CursorPoll"); socketPoll->startThread(); std::shared_ptr socket = helpers::loadDocAndGetSession(socketPoll, uri, documentURL, testname); // Insert comment. helpers::sendTextFrame(socket, "uno .uno:InsertAnnotation", testname); helpers::assertResponseString(socket, "invalidatetiles:", testname); // Paste some text. helpers::sendTextFrame(socket, "paste mimetype=text/plain;charset=utf-8\nxxx yyy zzzz", testname); // Read it back. std::string res = helpers::getAllText(socket, testname); LOK_ASSERT_EQUAL(std::string("textselectioncontent: xxx yyy zzzz"), res); // Can we edit the comment? helpers::sendTextFrame(socket, "paste mimetype=text/plain;charset=utf-8\naaa bbb ccc", testname); res = helpers::getAllText(socket, testname); LOK_ASSERT_EQUAL(std::string("textselectioncontent: aaa bbb ccc"), res); // Confirm that the text is in the comment and not doc body. // Click in the body. helpers::sendTextFrame( socket, "mouse type=buttondown x=1600 y=1600 count=1 buttons=1 modifier=0", testname); helpers::sendTextFrame(socket, "mouse type=buttonup x=1600 y=1600 count=1 buttons=1 modifier=0", testname); // Read body text. res = helpers::getAllText(socket, testname); LOK_ASSERT_EQUAL(std::string("textselectioncontent: Hello world"), res); // Confirm that the comment is still intact. helpers::sendTextFrame( socket, "mouse type=buttondown x=13855 y=1893 count=1 buttons=1 modifier=0", testname); helpers::sendTextFrame( socket, "mouse type=buttonup x=13855 y=1893 count=1 buttons=1 modifier=0", testname); res = helpers::getAllText(socket, testname); LOK_ASSERT_EQUAL(std::string("textselectioncontent: aaa bbb ccc"), res); // Can we still edit the comment? helpers::sendTextFrame( socket, "paste mimetype=text/plain;charset=utf-8\nand now for something completely different", testname); res = helpers::getAllText(socket, testname); LOK_ASSERT_EQUAL( std::string("textselectioncontent: and now for something completely different"), res); // Close and reopen the same document and test again. socket->shutdownWS(); LOK_ASSERT_MESSAGE("Expected successful disconnection of the WebSocket", socket->waitForDisconnection(std::chrono::seconds(5))); TST_LOG("Reloading "); socket = helpers::loadDocAndGetSession(socketPoll, uri, documentURL, testname); // Confirm that the text is in the comment and not doc body. // Click in the body. helpers::sendTextFrame( socket, "mouse type=buttondown x=1600 y=1600 count=1 buttons=1 modifier=0", testname); helpers::sendTextFrame(socket, "mouse type=buttonup x=1600 y=1600 count=1 buttons=1 modifier=0", testname); // Read body text. res = helpers::getAllText(socket, testname); LOK_ASSERT_EQUAL(std::string("textselectioncontent: Hello world"), res); // Confirm that the comment is still intact. helpers::sendTextFrame( socket, "mouse type=buttondown x=13855 y=1893 count=1 buttons=1 modifier=0", testname); helpers::sendTextFrame( socket, "mouse type=buttonup x=13855 y=1893 count=1 buttons=1 modifier=0", testname); res = helpers::getAllText(socket, testname); LOK_ASSERT_EQUAL( std::string("textselectioncontent: and now for something completely different"), res); // Can we still edit the comment? helpers::sendTextFrame(socket, "paste mimetype=text/plain;charset=utf-8\nblah blah xyz", testname); res = helpers::getAllText(socket, testname); LOK_ASSERT_EQUAL(std::string("textselectioncontent: blah blah xyz"), res); return TestResult::Ok; } UnitBase::TestResult UnitCursor::testEditAnnotationWriter() { std::string documentPath, documentURL; helpers::getDocumentPathAndURL("with_comment.odt", documentPath, documentURL, testname); Poco::URI uri(helpers::getTestServerURI()); std::shared_ptr socketPoll = std::make_shared("CursorPoll"); socketPoll->startThread(); std::shared_ptr socket = helpers::loadDocAndGetSession(socketPoll, uri, documentURL, testname); // Click in the body. helpers::sendTextFrame( socket, "mouse type=buttondown x=1600 y=1600 count=1 buttons=1 modifier=0", testname); helpers::sendTextFrame(socket, "mouse type=buttonup x=1600 y=1600 count=1 buttons=1 modifier=0", testname); // Read body text. std::string res = helpers::getAllText(socket, testname); LOK_ASSERT_EQUAL(std::string("textselectioncontent: Hello world"), res); // Confirm that the comment is intact. helpers::sendTextFrame( socket, "mouse type=buttondown x=13855 y=1893 count=1 buttons=1 modifier=0", testname); helpers::sendTextFrame( socket, "mouse type=buttonup x=13855 y=1893 count=1 buttons=1 modifier=0", testname); res = helpers::getAllText(socket, testname); LOK_ASSERT_EQUAL(std::string("textselectioncontent: blah blah xyz"), res); // Can we still edit the comment? helpers::sendTextFrame( socket, "paste mimetype=text/plain;charset=utf-8\nand now for something completely different", testname); res = helpers::getAllText(socket, testname); LOK_ASSERT_EQUAL( std::string("textselectioncontent: and now for something completely different"), res); // Close and reopen the same document and test again. TST_LOG("Closing connection after pasting."); socket->shutdownWS(); LOK_ASSERT_MESSAGE("Expected successful disconnection of the WebSocket", socket->waitForDisconnection(std::chrono::seconds(5))); TST_LOG("Reloading "); socket = helpers::loadDocAndGetSession(socketPoll, uri, documentURL, testname); // Confirm that the text is in the comment and not doc body. // Click in the body. helpers::sendTextFrame( socket, "mouse type=buttondown x=1600 y=1600 count=1 buttons=1 modifier=0", testname); helpers::sendTextFrame(socket, "mouse type=buttonup x=1600 y=1600 count=1 buttons=1 modifier=0", testname); // Read body text. res = helpers::getAllText(socket, testname); LOK_ASSERT_EQUAL(std::string("textselectioncontent: Hello world"), res); // Confirm that the comment is still intact. helpers::sendTextFrame( socket, "mouse type=buttondown x=13855 y=1893 count=1 buttons=1 modifier=0", testname); helpers::sendTextFrame( socket, "mouse type=buttonup x=13855 y=1893 count=1 buttons=1 modifier=0", testname); res = helpers::getAllText(socket, testname); LOK_ASSERT_EQUAL( std::string("textselectioncontent: and now for something completely different"), res); // Can we still edit the comment? helpers::sendTextFrame(socket, "paste mimetype=text/plain;charset=utf-8\nnew text different", testname); res = helpers::getAllText(socket, testname); LOK_ASSERT_EQUAL(std::string("textselectioncontent: new text different"), res); return TestResult::Ok; } UnitBase::TestResult UnitCursor::testInsertAnnotationCalc() { Poco::URI uri(helpers::getTestServerURI()); std::shared_ptr socketPoll = std::make_shared("CursorPoll"); socketPoll->startThread(); std::shared_ptr socket = helpers::loadDocAndGetSession(socketPoll, "setclientpart.ods", uri, testname); // Insert comment. helpers::sendTextFrame(socket, "uno .uno:InsertAnnotation", testname); // Paste some text. helpers::sendTextFrame(socket, "paste mimetype=text/plain;charset=utf-8\naaa bbb ccc", testname); // Read it back. std::string res = helpers::getAllText(socket, testname); LOK_ASSERT_EQUAL(std::string("textselectioncontent: aaa bbb ccc"), res); return TestResult::Ok; } void UnitCursor::invokeWSDTest() { UnitBase::TestResult result = testMaxColumn(); if (result != TestResult::Ok) exitTest(result); result = testMaxRow(); if (result != TestResult::Ok) exitTest(result); // result = testInsertAnnotationWriter(); // if (result != TestResult::Ok) // exitTest(result); // result = testEditAnnotationWriter(); // if (result != TestResult::Ok) // exitTest(result); // FIXME result = testInsertAnnotationCalc(); // if (result != TestResult::Ok) // exitTest(result); exitTest(TestResult::Ok); } UnitBase* unit_create_wsd(void) { return new UnitCursor(); } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */