b5f78fe73d
So that they are in-process, which means it's easier to debug when they fail. Also, in UnitLoad, give up on trying to avoid the sleep after disconnecting, it seems the old condition was not reliable. Change-Id: I972a3319887a70eeea2585104ed1e762830ca505
486 lines
17 KiB
C++
486 lines
17 KiB
C++
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
|
|
/*
|
|
* This file is part of the LibreOffice project.
|
|
*
|
|
* 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 <memory>
|
|
#include <ostream>
|
|
#include <set>
|
|
#include <string>
|
|
|
|
#include <Poco/Exception.h>
|
|
#include <Poco/RegularExpression.h>
|
|
#include <Poco/URI.h>
|
|
#include <cppunit/TestAssert.h>
|
|
|
|
#include <Png.hpp>
|
|
#include <Unit.hpp>
|
|
#include <helpers.hpp>
|
|
|
|
class LOOLWebSocket;
|
|
|
|
namespace
|
|
{
|
|
double getColRowSize(const std::string& property, const std::string& message, int index)
|
|
{
|
|
Poco::JSON::Parser parser;
|
|
const Poco::Dynamic::Var result = parser.parse(message);
|
|
const auto& command = result.extract<Poco::JSON::Object::Ptr>();
|
|
std::string text = command->get("commandName").toString();
|
|
|
|
CPPUNIT_ASSERT_EQUAL(std::string(".uno:ViewRowColumnHeaders"), text);
|
|
CPPUNIT_ASSERT(command->isArray(property));
|
|
|
|
Poco::JSON::Array::Ptr array = command->getArray(property);
|
|
|
|
CPPUNIT_ASSERT(array->isObject(index));
|
|
|
|
Poco::SharedPtr<Poco::JSON::Object> item = array->getObject(index);
|
|
|
|
CPPUNIT_ASSERT(item->has("size"));
|
|
|
|
return item->getValue<double>("size");
|
|
}
|
|
|
|
double getColRowSize(const std::shared_ptr<LOOLWebSocket>& socket, const std::string& item,
|
|
int index, const std::string& testname)
|
|
{
|
|
std::vector<char> response;
|
|
response = helpers::getResponseMessage(socket, "commandvalues:", testname);
|
|
CPPUNIT_ASSERT_MESSAGE("did not receive a commandvalues: message as expected",
|
|
!response.empty());
|
|
std::vector<char> json(response.begin() + std::string("commandvalues:").length(),
|
|
response.end());
|
|
json.push_back(0);
|
|
return getColRowSize(item, json.data(), index);
|
|
}
|
|
}
|
|
|
|
/// Test suite for Calc.
|
|
class UnitCalc : public UnitWSD
|
|
{
|
|
TestResult testCalcEditRendering();
|
|
TestResult testCalcRenderAfterNewView51();
|
|
TestResult testCalcRenderAfterNewView53();
|
|
TestResult testColumnRowResize();
|
|
TestResult testOptimalResize();
|
|
|
|
public:
|
|
void invokeTest() override;
|
|
};
|
|
|
|
UnitBase::TestResult UnitCalc::testCalcEditRendering()
|
|
{
|
|
const char* testname = "calcEditRendering ";
|
|
Poco::URI uri(helpers::getTestServerURI());
|
|
std::shared_ptr<LOOLWebSocket> socket
|
|
= helpers::loadDocAndGetSocket("calc_render.xls", uri, testname);
|
|
|
|
helpers::sendTextFrame(socket, "mouse type=buttondown x=5000 y=5 count=1 buttons=1 modifier=0",
|
|
testname);
|
|
helpers::sendTextFrame(socket, "key type=input char=97 key=0", testname);
|
|
helpers::sendTextFrame(socket, "key type=input char=98 key=0", testname);
|
|
helpers::sendTextFrame(socket, "key type=input char=99 key=0", testname);
|
|
|
|
helpers::assertResponseString(socket, "cellformula: abc", testname);
|
|
|
|
const char* req = "tilecombine nviewid=0 part=0 width=512 height=512 tileposx=3840 tileposy=0 "
|
|
"tilewidth=7680 tileheight=7680";
|
|
helpers::sendTextFrame(socket, req, testname);
|
|
|
|
const std::vector<char> tile = helpers::getResponseMessage(socket, "tile:", testname);
|
|
TST_LOG("size: " << tile.size());
|
|
|
|
// Return early for now when on LO >= 5.2.
|
|
int major = 0;
|
|
int minor = 0;
|
|
helpers::getServerVersion(socket, major, minor, testname);
|
|
|
|
const std::string firstLine = LOOLProtocol::getFirstLine(tile);
|
|
std::vector<char> res(tile.begin() + firstLine.size() + 1, tile.end());
|
|
std::stringstream streamRes;
|
|
std::copy(res.begin(), res.end(), std::ostream_iterator<char>(streamRes));
|
|
|
|
std::fstream outStream("/tmp/res.png", std::ios::out);
|
|
outStream.write(res.data(), res.size());
|
|
outStream.close();
|
|
|
|
png_uint_32 height = 0;
|
|
png_uint_32 width = 0;
|
|
png_uint_32 rowBytes = 0;
|
|
std::vector<png_bytep> rows = Png::decodePNG(streamRes, height, width, rowBytes);
|
|
|
|
const std::vector<char> exp
|
|
= helpers::readDataFromFile("calc_render_0_512x512.3840,0.7680x7680.png");
|
|
std::stringstream streamExp;
|
|
std::copy(exp.begin(), exp.end(), std::ostream_iterator<char>(streamExp));
|
|
|
|
png_uint_32 heightExp = 0;
|
|
png_uint_32 widthExp = 0;
|
|
png_uint_32 rowBytesExp = 0;
|
|
std::vector<png_bytep> rowsExp = Png::decodePNG(streamExp, heightExp, widthExp, rowBytesExp);
|
|
|
|
CPPUNIT_ASSERT_EQUAL(heightExp, height);
|
|
CPPUNIT_ASSERT_EQUAL(widthExp, width);
|
|
CPPUNIT_ASSERT_EQUAL(rowBytesExp, rowBytes);
|
|
|
|
for (png_uint_32 itRow = 0; itRow < height; ++itRow)
|
|
{
|
|
const bool eq = std::equal(rowsExp[itRow], rowsExp[itRow] + rowBytes, rows[itRow]);
|
|
if (!eq)
|
|
{
|
|
// This is a very strict test that breaks often/easily due to slight rendering
|
|
// differences. So for now just keep it informative only.
|
|
//CPPUNIT_ASSERT_MESSAGE("Tile not rendered as expected @ row #" + std::to_string(itRow), eq);
|
|
TST_LOG("\nFAILURE: Tile not rendered as expected @ row #" << itRow);
|
|
break;
|
|
}
|
|
}
|
|
return TestResult::Ok;
|
|
}
|
|
|
|
/// When a second view is loaded to a Calc doc,
|
|
/// the first stops rendering correctly.
|
|
/// This only happens at high rows.
|
|
UnitBase::TestResult UnitCalc::testCalcRenderAfterNewView51()
|
|
{
|
|
const char* testname = "calcRenderAfterNewView51 ";
|
|
|
|
// Load a doc with the cursor saved at a top row.
|
|
std::string documentPath, documentURL;
|
|
helpers::getDocumentPathAndURL("empty.ods", documentPath, documentURL, testname);
|
|
|
|
Poco::URI uri(helpers::getTestServerURI());
|
|
std::shared_ptr<LOOLWebSocket> socket
|
|
= helpers::loadDocAndGetSocket(uri, documentURL, testname);
|
|
|
|
int major = 0;
|
|
int minor = 0;
|
|
helpers::getServerVersion(socket, major, minor, testname);
|
|
if (major != 5 || minor != 1)
|
|
{
|
|
TST_LOG("Skipping test on incompatible client [" << major << '.' << minor
|
|
<< "], expected [5.1].");
|
|
return TestResult::Ok;
|
|
}
|
|
|
|
// Page Down until we get to the bottom of the doc.
|
|
for (int i = 0; i < 40; ++i)
|
|
{
|
|
helpers::sendTextFrame(socket, "key type=input char=0 key=1031", testname);
|
|
}
|
|
|
|
// Wait for status due to doc resize.
|
|
helpers::assertResponseString(socket, "status:", testname);
|
|
|
|
const char* req = "tilecombine nviewid=0 part=0 width=256 height=256 tileposx=0 "
|
|
"tileposy=253440 tilewidth=3840 tileheight=3840";
|
|
|
|
// Get tile.
|
|
const std::vector<char> tile1
|
|
= helpers::getTileAndSave(socket, req, "/tmp/calc_render_51_orig.png", testname);
|
|
|
|
// Connect second client, which will load at the top.
|
|
TST_LOG("Connecting second client.");
|
|
std::shared_ptr<LOOLWebSocket> socket2
|
|
= helpers::loadDocAndGetSocket(uri, documentURL, testname);
|
|
|
|
// Up one row on the first view to trigger the bug.
|
|
TST_LOG("Up.");
|
|
helpers::sendTextFrame(socket, "key type=input char=0 key=1025", testname);
|
|
helpers::assertResponseString(socket, "invalidatetiles:", testname); // Up invalidates.
|
|
|
|
// Get same tile again.
|
|
const std::vector<char> tile2
|
|
= helpers::getTileAndSave(socket, req, "/tmp/calc_render_51_sec.png", testname);
|
|
|
|
CPPUNIT_ASSERT(tile1 == tile2);
|
|
return TestResult::Ok;
|
|
}
|
|
|
|
UnitBase::TestResult UnitCalc::testCalcRenderAfterNewView53()
|
|
{
|
|
const char* testname = "calcRenderAfterNewView53 ";
|
|
|
|
// Load a doc with the cursor saved at a top row.
|
|
std::string documentPath, documentURL;
|
|
helpers::getDocumentPathAndURL("calc-render.ods", documentPath, documentURL, testname);
|
|
|
|
Poco::URI uri(helpers::getTestServerURI());
|
|
std::shared_ptr<LOOLWebSocket> socket
|
|
= helpers::loadDocAndGetSocket(uri, documentURL, testname);
|
|
|
|
int major = 0;
|
|
int minor = 0;
|
|
helpers::getServerVersion(socket, major, minor, testname);
|
|
if (major < 5 || minor < 3)
|
|
{
|
|
TST_LOG("Skipping test on incompatible client [" << major << '.' << minor
|
|
<< "], expected [>=5.3].");
|
|
return TestResult::Ok;
|
|
}
|
|
|
|
helpers::sendTextFrame(socket, "clientvisiblearea x=750 y=1861 width=20583 height=6997",
|
|
testname);
|
|
helpers::sendTextFrame(socket, "key type=input char=0 key=1031", testname);
|
|
|
|
// Get tile.
|
|
const char* req = "tilecombine nviewid=0 part=0 width=256 height=256 tileposx=0 "
|
|
"tileposy=291840 tilewidth=3840 tileheight=3840 oldwid=0";
|
|
const std::vector<char> tile1
|
|
= helpers::getTileAndSave(socket, req, "/tmp/calc_render_53_orig.png", testname);
|
|
|
|
// Connect second client, which will load at the top.
|
|
TST_LOG("Connecting second client.");
|
|
std::shared_ptr<LOOLWebSocket> socket2
|
|
= helpers::loadDocAndGetSocket(uri, documentURL, testname);
|
|
|
|
TST_LOG("Waiting for cellviewcursor of second on first.");
|
|
helpers::assertResponseString(socket, "cellviewcursor:", testname);
|
|
|
|
// Get same tile again.
|
|
const std::vector<char> tile2
|
|
= helpers::getTileAndSave(socket, req, "/tmp/calc_render_53_sec.png", testname);
|
|
|
|
CPPUNIT_ASSERT(tile1 == tile2);
|
|
|
|
// Don't let them go out of scope and disconnect.
|
|
socket2->shutdown();
|
|
socket->shutdown();
|
|
return TestResult::Ok;
|
|
}
|
|
|
|
UnitBase::TestResult UnitCalc::testColumnRowResize()
|
|
{
|
|
const char* testname = "columnRowResize ";
|
|
try
|
|
{
|
|
std::vector<char> response;
|
|
std::string documentPath, documentURL;
|
|
double oldHeight, oldWidth;
|
|
|
|
helpers::getDocumentPathAndURL("setclientpart.ods", documentPath, documentURL, testname);
|
|
Poco::URI uri(helpers::getTestServerURI());
|
|
std::shared_ptr<LOOLWebSocket> socket
|
|
= helpers::loadDocAndGetSocket(uri, documentURL, testname);
|
|
|
|
const std::string commandValues = "commandvalues command=.uno:ViewRowColumnHeaders";
|
|
helpers::sendTextFrame(socket, commandValues);
|
|
response = helpers::getResponseMessage(socket, "commandvalues:", testname);
|
|
CPPUNIT_ASSERT_MESSAGE("did not receive a commandvalues: message as expected",
|
|
!response.empty());
|
|
{
|
|
std::vector<char> json(response.begin() + std::string("commandvalues:").length(),
|
|
response.end());
|
|
json.push_back(0);
|
|
|
|
// get column 2
|
|
oldHeight = getColRowSize("rows", json.data(), 1);
|
|
// get row 2
|
|
oldWidth = getColRowSize("columns", json.data(), 1);
|
|
}
|
|
|
|
// send column width
|
|
{
|
|
std::ostringstream oss;
|
|
Poco::JSON::Object objJSON, objColumn, objWidth;
|
|
double newWidth;
|
|
|
|
// change column 2
|
|
objColumn.set("type", "unsigned short");
|
|
objColumn.set("value", 2);
|
|
|
|
objWidth.set("type", "unsigned short");
|
|
objWidth.set("value", oldWidth + 100);
|
|
|
|
objJSON.set("Column", objColumn);
|
|
objJSON.set("Width", objWidth);
|
|
|
|
Poco::JSON::Stringifier::stringify(objJSON, oss);
|
|
helpers::sendTextFrame(socket, "uno .uno:ColumnWidth " + oss.str(), testname);
|
|
helpers::sendTextFrame(socket, commandValues, testname);
|
|
response = helpers::getResponseMessage(socket, "commandvalues:", testname);
|
|
CPPUNIT_ASSERT_MESSAGE("did not receive a commandvalues: message as expected",
|
|
!response.empty());
|
|
std::vector<char> json(response.begin() + std::string("commandvalues:").length(),
|
|
response.end());
|
|
json.push_back(0);
|
|
newWidth = getColRowSize("columns", json.data(), 1);
|
|
CPPUNIT_ASSERT(newWidth > oldWidth);
|
|
}
|
|
|
|
// send row height
|
|
{
|
|
std::ostringstream oss;
|
|
Poco::JSON::Object objJSON, objRow, objHeight;
|
|
double newHeight;
|
|
|
|
// change row 2
|
|
objRow.set("type", "unsigned short");
|
|
objRow.set("value", 2);
|
|
|
|
objHeight.set("type", "unsigned short");
|
|
objHeight.set("value", oldHeight + 100);
|
|
|
|
objJSON.set("Row", objRow);
|
|
objJSON.set("Height", objHeight);
|
|
|
|
Poco::JSON::Stringifier::stringify(objJSON, oss);
|
|
helpers::sendTextFrame(socket, "uno .uno:RowHeight " + oss.str(), testname);
|
|
helpers::sendTextFrame(socket, commandValues, testname);
|
|
response = helpers::getResponseMessage(socket, "commandvalues:", testname);
|
|
CPPUNIT_ASSERT_MESSAGE("did not receive a commandvalues: message as expected",
|
|
!response.empty());
|
|
std::vector<char> json(response.begin() + std::string("commandvalues:").length(),
|
|
response.end());
|
|
json.push_back(0);
|
|
newHeight = getColRowSize("rows", json.data(), 1);
|
|
CPPUNIT_ASSERT(newHeight > oldHeight);
|
|
}
|
|
}
|
|
catch (const Poco::Exception& exc)
|
|
{
|
|
CPPUNIT_FAIL(exc.displayText());
|
|
}
|
|
return TestResult::Ok;
|
|
}
|
|
|
|
UnitBase::TestResult UnitCalc::testOptimalResize()
|
|
{
|
|
const char* testname = "optimalResize ";
|
|
try
|
|
{
|
|
double newWidth, newHeight;
|
|
Poco::JSON::Object objIndex, objSize, objModifier;
|
|
|
|
// row/column index 0
|
|
objIndex.set("type", "unsigned short");
|
|
objIndex.set("value", 1);
|
|
|
|
// size in twips
|
|
objSize.set("type", "unsigned short");
|
|
objSize.set("value", 3840);
|
|
|
|
// keyboard modifier
|
|
objModifier.set("type", "unsigned short");
|
|
objModifier.set("value", 0);
|
|
|
|
std::string documentPath, documentURL;
|
|
helpers::getDocumentPathAndURL("empty.ods", documentPath, documentURL, testname);
|
|
Poco::URI uri(helpers::getTestServerURI());
|
|
std::shared_ptr<LOOLWebSocket> socket
|
|
= helpers::loadDocAndGetSocket(uri, documentURL, testname);
|
|
|
|
const std::string commandValues = "commandvalues command=.uno:ViewRowColumnHeaders";
|
|
// send new column width
|
|
{
|
|
std::ostringstream oss;
|
|
Poco::JSON::Object objJSON;
|
|
|
|
objJSON.set("Column", objIndex);
|
|
objJSON.set("Width", objSize);
|
|
|
|
Poco::JSON::Stringifier::stringify(objJSON, oss);
|
|
helpers::sendTextFrame(socket, "uno .uno:ColumnWidth " + oss.str(), testname);
|
|
helpers::sendTextFrame(socket, commandValues, testname);
|
|
newWidth = getColRowSize(socket, "columns", 0, testname);
|
|
}
|
|
// send new row height
|
|
{
|
|
std::ostringstream oss;
|
|
Poco::JSON::Object objJSON;
|
|
|
|
objJSON.set("Row", objIndex);
|
|
objJSON.set("Height", objSize);
|
|
|
|
Poco::JSON::Stringifier::stringify(objJSON, oss);
|
|
helpers::sendTextFrame(socket, "uno .uno:RowHeight " + oss.str(), testname);
|
|
helpers::sendTextFrame(socket, commandValues, testname);
|
|
newHeight = getColRowSize(socket, "rows", 0, testname);
|
|
}
|
|
|
|
objIndex.set("value", 0);
|
|
|
|
// send optimal column width
|
|
{
|
|
std::ostringstream oss;
|
|
Poco::JSON::Object objJSON;
|
|
double optimalWidth;
|
|
|
|
objJSON.set("Col", objIndex);
|
|
objJSON.set("Modifier", objModifier);
|
|
|
|
Poco::JSON::Stringifier::stringify(objJSON, oss);
|
|
helpers::sendTextFrame(socket, "uno .uno:SelectColumn " + oss.str(), testname);
|
|
helpers::sendTextFrame(socket, "uno .uno:SetOptimalColumnWidthDirect", testname);
|
|
helpers::sendTextFrame(socket, commandValues, testname);
|
|
optimalWidth = getColRowSize(socket, "columns", 0, testname);
|
|
CPPUNIT_ASSERT(optimalWidth < newWidth);
|
|
}
|
|
|
|
// send optimal row height
|
|
{
|
|
Poco::JSON::Object objSelect, objOptHeight, objExtra;
|
|
double optimalHeight;
|
|
|
|
objSelect.set("Row", objIndex);
|
|
objSelect.set("Modifier", objModifier);
|
|
|
|
objExtra.set("type", "unsigned short");
|
|
objExtra.set("value", 0);
|
|
|
|
objOptHeight.set("aExtraHeight", objExtra);
|
|
|
|
std::ostringstream oss;
|
|
Poco::JSON::Stringifier::stringify(objSelect, oss);
|
|
helpers::sendTextFrame(socket, "uno .uno:SelectRow " + oss.str(), testname);
|
|
oss.str("");
|
|
oss.clear();
|
|
|
|
Poco::JSON::Stringifier::stringify(objOptHeight, oss);
|
|
helpers::sendTextFrame(socket, "uno .uno:SetOptimalRowHeight " + oss.str(), testname);
|
|
|
|
helpers::sendTextFrame(socket, commandValues, testname);
|
|
optimalHeight = getColRowSize(socket, "rows", 0, testname);
|
|
CPPUNIT_ASSERT(optimalHeight < newHeight);
|
|
}
|
|
}
|
|
catch (const Poco::Exception& exc)
|
|
{
|
|
CPPUNIT_FAIL(exc.displayText());
|
|
}
|
|
return TestResult::Ok;
|
|
}
|
|
|
|
void UnitCalc::invokeTest()
|
|
{
|
|
UnitBase::TestResult result = testCalcEditRendering();
|
|
if (result != TestResult::Ok)
|
|
exitTest(result);
|
|
|
|
result = testCalcRenderAfterNewView51();
|
|
if (result != TestResult::Ok)
|
|
exitTest(result);
|
|
|
|
result = testCalcRenderAfterNewView53();
|
|
if (result != TestResult::Ok)
|
|
exitTest(result);
|
|
|
|
// FIXME result = testColumnRowResize();
|
|
if (result != TestResult::Ok)
|
|
exitTest(result);
|
|
|
|
// FIXME result = testOptimalResize();
|
|
if (result != TestResult::Ok)
|
|
exitTest(result);
|
|
|
|
exitTest(TestResult::Ok);
|
|
}
|
|
|
|
UnitBase* unit_create_wsd(void) { return new UnitCalc(); }
|
|
|
|
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|