82560d9657
Because the new-style tests are intrustive, the exception that CppUnit throws on assertion failures is caught and processed with the application logic, which is far from ideal, because it's very difficult to find the cause of failure. What we'd like is a way to control what happens when an test assertion fails, such that we can properly log/print the failure, and even break in the debugger. The new macros allow us to control the behavior at compile-time and have added flexibility. For now, they log an assertion failure before invoking the CPPUNIT macro, and support a compile-time directive to assert, which is useful for breaking in the debugger. Change-Id: If464ba246e3ec747f31496a4215cb73ef735dfaf Reviewed-on: https://gerrit.libreoffice.org/c/online/+/87625 Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoffice@gmail.com> Reviewed-by: Michael Meeks <michael.meeks@collabora.com>
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 <test/lokassert.hpp>
|
|
|
|
#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();
|
|
|
|
LOK_ASSERT_EQUAL(std::string(".uno:ViewRowColumnHeaders"), text);
|
|
LOK_ASSERT(command->isArray(property));
|
|
|
|
Poco::JSON::Array::Ptr array = command->getArray(property);
|
|
|
|
LOK_ASSERT(array->isObject(index));
|
|
|
|
Poco::SharedPtr<Poco::JSON::Object> item = array->getObject(index);
|
|
|
|
LOK_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);
|
|
LOK_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);
|
|
|
|
LOK_ASSERT_EQUAL(heightExp, height);
|
|
LOK_ASSERT_EQUAL(widthExp, width);
|
|
LOK_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.
|
|
//LOK_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);
|
|
|
|
LOK_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);
|
|
|
|
LOK_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);
|
|
LOK_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);
|
|
LOK_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);
|
|
LOK_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);
|
|
LOK_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);
|
|
LOK_ASSERT(newHeight > oldHeight);
|
|
}
|
|
}
|
|
catch (const Poco::Exception& exc)
|
|
{
|
|
LOK_ASSERT_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);
|
|
LOK_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);
|
|
LOK_ASSERT(optimalHeight < newHeight);
|
|
}
|
|
}
|
|
catch (const Poco::Exception& exc)
|
|
{
|
|
LOK_ASSERT_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: */
|