libreoffice-online/test/TileQueueTests.cpp
Jan Holesovsky c44f7b8a76 Revert "loolwsd: improved MessageQueue"
Now we don't get a situation where there would be a tremendous amount of
invalidates & tile render requests piled in the queue, so we can do it
deterministic again.

The only thing that could potentially pile in the queue are the keypresses
events sent from the clients, but that is a different problem anyway.

This reverts commit c326228774.

Change-Id: I98e199eab0187bf5f47ce322ac1b1b2e3b976b85
2017-01-26 11:49:29 +01:00

449 lines
16 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 "config.h"
#include <cppunit/extensions/HelperMacros.h>
#include "Common.hpp"
#include "Protocol.hpp"
#include "Message.hpp"
#include "MessageQueue.hpp"
#include "SenderQueue.hpp"
#include "Util.hpp"
namespace CPPUNIT_NS
{
template<>
struct assertion_traits<std::vector<char>>
{
static bool equal(const std::vector<char>& x, const std::vector<char>& y)
{
return x == y;
}
static std::string toString(const std::vector<char>& x)
{
const std::string text = '"' + (!x.empty() ? std::string(x.data(), x.size()) : "<empty>") + '"';
return text;
}
};
}
/// TileQueue unit-tests.
class TileQueueTests : public CPPUNIT_NS::TestFixture
{
CPPUNIT_TEST_SUITE(TileQueueTests);
CPPUNIT_TEST(testTileQueuePriority);
CPPUNIT_TEST(testTileCombinedRendering);
CPPUNIT_TEST(testTileRecombining);
CPPUNIT_TEST(testViewOrder);
CPPUNIT_TEST(testPreviewsDeprioritization);
CPPUNIT_TEST(testSenderQueue);
CPPUNIT_TEST(testSenderQueueTileDeduplication);
CPPUNIT_TEST(testInvalidateViewCursorDeduplication);
CPPUNIT_TEST(testCallbackInvalidation);
CPPUNIT_TEST_SUITE_END();
void testTileQueuePriority();
void testTileCombinedRendering();
void testTileRecombining();
void testViewOrder();
void testPreviewsDeprioritization();
void testSenderQueue();
void testSenderQueueTileDeduplication();
void testInvalidateViewCursorDeduplication();
void testCallbackInvalidation();
};
void TileQueueTests::testTileQueuePriority()
{
const std::string reqHigh = "tile part=0 width=256 height=256 tileposx=0 tileposy=0 tilewidth=3840 tileheight=3840 oldhash=0 hash=0";
const std::string resHigh = "tile part=0 width=256 height=256 tileposx=0 tileposy=0 tilewidth=3840 tileheight=3840 oldhash=0 hash=0 ver=-1";
const TileQueue::Payload payloadHigh(resHigh.data(), resHigh.data() + resHigh.size());
const std::string reqLow = "tile part=0 width=256 height=256 tileposx=0 tileposy=253440 tilewidth=3840 tileheight=3840 oldhash=0 hash=0";
const std::string resLow = "tile part=0 width=256 height=256 tileposx=0 tileposy=253440 tilewidth=3840 tileheight=3840 oldhash=0 hash=0 ver=-1";
const TileQueue::Payload payloadLow(resLow.data(), resLow.data() + resLow.size());
TileQueue queue;
// Request the tiles.
queue.put(reqLow);
queue.put(reqHigh);
// Original order.
CPPUNIT_ASSERT_EQUAL(payloadLow, queue.get());
CPPUNIT_ASSERT_EQUAL(payloadHigh, queue.get());
// Request the tiles.
queue.put(reqLow);
queue.put(reqHigh);
queue.put(reqHigh);
queue.put(reqLow);
// Set cursor above reqHigh.
queue.updateCursorPosition(0, 0, 0, 0, 10, 100);
// Prioritized order.
CPPUNIT_ASSERT_EQUAL(payloadHigh, queue.get());
CPPUNIT_ASSERT_EQUAL(payloadLow, queue.get());
// Repeat with cursor position set.
queue.put(reqLow);
queue.put(reqHigh);
CPPUNIT_ASSERT_EQUAL(payloadHigh, queue.get());
CPPUNIT_ASSERT_EQUAL(payloadLow, queue.get());
// Repeat by changing cursor position.
queue.put(reqLow);
queue.put(reqHigh);
queue.updateCursorPosition(0, 0, 0, 253450, 10, 100);
CPPUNIT_ASSERT_EQUAL(payloadLow, queue.get());
CPPUNIT_ASSERT_EQUAL(payloadHigh, queue.get());
}
void TileQueueTests::testTileCombinedRendering()
{
const std::string req1 = "tile part=0 width=256 height=256 tileposx=0 tileposy=0 tilewidth=3840 tileheight=3840";
const std::string req2 = "tile part=0 width=256 height=256 tileposx=3840 tileposy=0 tilewidth=3840 tileheight=3840";
const std::string req3 = "tile part=0 width=256 height=256 tileposx=0 tileposy=3840 tilewidth=3840 tileheight=3840";
const std::string req4 = "tile part=0 width=256 height=256 tileposx=3840 tileposy=3840 tilewidth=3840 tileheight=3840";
const std::string resHor = "tilecombine part=0 width=256 height=256 tileposx=0,3840 tileposy=0,0 imgsize=0,0 tilewidth=3840 tileheight=3840 ver=-1,-1 oldhash=0,0 hash=0,0";
const TileQueue::Payload payloadHor(resHor.data(), resHor.data() + resHor.size());
const std::string resVer = "tilecombine part=0 width=256 height=256 tileposx=0,0 tileposy=0,3840 imgsize=0,0 tilewidth=3840 tileheight=3840 ver=-1,-1 oldhash=0,0 hash=0,0";
const TileQueue::Payload payloadVer(resVer.data(), resVer.data() + resVer.size());
const std::string resFull = "tilecombine part=0 width=256 height=256 tileposx=0,3840,0 tileposy=0,0,3840 imgsize=0,0,0 tilewidth=3840 tileheight=3840 ver=-1,-1,-1 oldhash=0,0,0 hash=0,0,0";
const TileQueue::Payload payloadFull(resFull.data(), resFull.data() + resFull.size());
TileQueue queue;
// Horizontal.
queue.put(req1);
queue.put(req2);
CPPUNIT_ASSERT_EQUAL(payloadHor, queue.get());
// Vertical.
queue.put(req1);
queue.put(req3);
CPPUNIT_ASSERT_EQUAL(payloadVer, queue.get());
// Vertical.
queue.put(req1);
queue.put(req2);
queue.put(req3);
CPPUNIT_ASSERT_EQUAL(payloadFull, queue.get());
}
namespace {
std::string payloadAsString(const MessageQueue::Payload& payload)
{
return std::string(payload.data(), payload.size());
}
}
void TileQueueTests::testTileRecombining()
{
TileQueue queue;
queue.put("tilecombine part=0 width=256 height=256 tileposx=0,3840,7680 tileposy=0,0,0 tilewidth=3840 tileheight=3840");
queue.put("tilecombine part=0 width=256 height=256 tileposx=0,3840 tileposy=0,0 tilewidth=3840 tileheight=3840");
// the tilecombine's get merged, resulting in 3 "tile" messages
CPPUNIT_ASSERT_EQUAL(3, static_cast<int>(queue._queue.size()));
// but when we later extract that, it is just one "tilecombine" message
std::string message(payloadAsString(queue.get()));
CPPUNIT_ASSERT_EQUAL(std::string("tilecombine part=0 width=256 height=256 tileposx=7680,0,3840 tileposy=0,0,0 imgsize=0,0,0 tilewidth=3840 tileheight=3840 ver=-1,-1,-1 oldhash=0,0,0 hash=0,0,0"), message);
// and nothing remains in the queue
CPPUNIT_ASSERT_EQUAL(0, static_cast<int>(queue._queue.size()));
}
void TileQueueTests::testViewOrder()
{
TileQueue queue;
// should result in the 3, 2, 1, 0 order of the views
queue.updateCursorPosition(0, 0, 0, 0, 10, 100);
queue.updateCursorPosition(2, 0, 0, 0, 10, 100);
queue.updateCursorPosition(1, 0, 0, 7680, 10, 100);
queue.updateCursorPosition(3, 0, 0, 0, 10, 100);
queue.updateCursorPosition(2, 0, 0, 15360, 10, 100);
queue.updateCursorPosition(3, 0, 0, 23040, 10, 100);
const std::vector<std::string> tiles =
{
"tile part=0 width=256 height=256 tileposx=0 tileposy=0 tilewidth=3840 tileheight=3840 oldhash=0 hash=0 ver=-1",
"tile part=0 width=256 height=256 tileposx=0 tileposy=7680 tilewidth=3840 tileheight=3840 oldhash=0 hash=0 ver=-1",
"tile part=0 width=256 height=256 tileposx=0 tileposy=15360 tilewidth=3840 tileheight=3840 oldhash=0 hash=0 ver=-1",
"tile part=0 width=256 height=256 tileposx=0 tileposy=23040 tilewidth=3840 tileheight=3840 oldhash=0 hash=0 ver=-1"
};
for (auto &tile : tiles)
queue.put(tile);
CPPUNIT_ASSERT_EQUAL(4, static_cast<int>(queue._queue.size()));
// should result in the 3, 2, 1, 0 order of the tiles thanks to the cursor
// positions
for (size_t i = 0; i < tiles.size(); ++i)
{
CPPUNIT_ASSERT_EQUAL(tiles[3 - i], payloadAsString(queue.get()));
}
}
void TileQueueTests::testPreviewsDeprioritization()
{
TileQueue queue;
// simple case - put previews to the queue and get everything back again
const std::vector<std::string> previews =
{
"tile part=0 width=180 height=135 tileposx=0 tileposy=0 tilewidth=15875 tileheight=11906 ver=-1 id=0",
"tile part=1 width=180 height=135 tileposx=0 tileposy=0 tilewidth=15875 tileheight=11906 ver=-1 id=1",
"tile part=2 width=180 height=135 tileposx=0 tileposy=0 tilewidth=15875 tileheight=11906 ver=-1 id=2",
"tile part=3 width=180 height=135 tileposx=0 tileposy=0 tilewidth=15875 tileheight=11906 ver=-1 id=3"
};
for (auto &preview : previews)
queue.put(preview);
for (size_t i = 0; i < previews.size(); ++i)
{
CPPUNIT_ASSERT_EQUAL(previews[i], payloadAsString(queue.get()));
}
// stays empty after all is done
CPPUNIT_ASSERT_EQUAL(0, static_cast<int>(queue._queue.size()));
// re-ordering case - put previews and normal tiles to the queue and get
// everything back again but this time the tiles have to interleave with
// the previews
const std::vector<std::string> tiles =
{
"tile part=0 width=256 height=256 tileposx=0 tileposy=0 tilewidth=3840 tileheight=3840 oldhash=0 hash=0 ver=-1",
"tile part=0 width=256 height=256 tileposx=0 tileposy=7680 tilewidth=3840 tileheight=3840 oldhash=0 hash=0 ver=-1"
};
for (auto &preview : previews)
queue.put(preview);
queue.put(tiles[0]);
CPPUNIT_ASSERT_EQUAL(previews[0], payloadAsString(queue.get()));
CPPUNIT_ASSERT_EQUAL(tiles[0], payloadAsString(queue.get()));
CPPUNIT_ASSERT_EQUAL(previews[1], payloadAsString(queue.get()));
queue.put(tiles[1]);
CPPUNIT_ASSERT_EQUAL(previews[2], payloadAsString(queue.get()));
CPPUNIT_ASSERT_EQUAL(tiles[1], payloadAsString(queue.get()));
CPPUNIT_ASSERT_EQUAL(previews[3], payloadAsString(queue.get()));
// stays empty after all is done
CPPUNIT_ASSERT_EQUAL(0, static_cast<int>(queue._queue.size()));
// cursor positioning case - the cursor position should not prioritize the
// previews
queue.updateCursorPosition(0, 0, 0, 0, 10, 100);
queue.put(tiles[1]);
queue.put(previews[0]);
CPPUNIT_ASSERT_EQUAL(tiles[1], payloadAsString(queue.get()));
CPPUNIT_ASSERT_EQUAL(previews[0], payloadAsString(queue.get()));
// stays empty after all is done
CPPUNIT_ASSERT_EQUAL(0, static_cast<int>(queue._queue.size()));
}
void TileQueueTests::testSenderQueue()
{
SenderQueue<std::shared_ptr<Message>> queue;
std::shared_ptr<Message> item;
// Empty queue
CPPUNIT_ASSERT_EQUAL(false, queue.waitDequeue(item, 10));
CPPUNIT_ASSERT_EQUAL(0UL, queue.size());
const std::vector<std::string> messages =
{
"message 1",
"message 2",
"message 3"
};
for (const auto& msg : messages)
{
queue.enqueue(std::make_shared<Message>(msg, Message::Dir::Out));
}
CPPUNIT_ASSERT_EQUAL(3UL, queue.size());
CPPUNIT_ASSERT_EQUAL(true, queue.waitDequeue(item, 0));
CPPUNIT_ASSERT_EQUAL(2UL, queue.size());
CPPUNIT_ASSERT_EQUAL(messages[0], std::string(item->data().data(), item->data().size()));
CPPUNIT_ASSERT_EQUAL(true, queue.waitDequeue(item, 0));
CPPUNIT_ASSERT_EQUAL(1UL, queue.size());
CPPUNIT_ASSERT_EQUAL(messages[1], std::string(item->data().data(), item->data().size()));
CPPUNIT_ASSERT_EQUAL(true, queue.waitDequeue(item, 0));
CPPUNIT_ASSERT_EQUAL(0UL, queue.size());
CPPUNIT_ASSERT_EQUAL(messages[2], std::string(item->data().data(), item->data().size()));
CPPUNIT_ASSERT_EQUAL(0UL, queue.size());
}
void TileQueueTests::testSenderQueueTileDeduplication()
{
SenderQueue<std::shared_ptr<Message>> queue;
std::shared_ptr<Message> item;
// Empty queue
CPPUNIT_ASSERT_EQUAL(false, queue.waitDequeue(item, 10));
CPPUNIT_ASSERT_EQUAL(0UL, queue.size());
const std::vector<std::string> part_messages =
{
"tile: part=0 width=180 height=135 tileposx=0 tileposy=0 tilewidth=15875 tileheight=11906 ver=0",
"tile: part=1 width=180 height=135 tileposx=0 tileposy=0 tilewidth=15875 tileheight=11906 ver=1",
"tile: part=2 width=180 height=135 tileposx=0 tileposy=0 tilewidth=15875 tileheight=11906 ver=-1"
};
for (const auto& msg : part_messages)
{
queue.enqueue(std::make_shared<Message>(msg, Message::Dir::Out));
}
CPPUNIT_ASSERT_EQUAL(3UL, queue.size());
CPPUNIT_ASSERT_EQUAL(true, queue.waitDequeue(item, 10));
CPPUNIT_ASSERT_EQUAL(true, queue.waitDequeue(item, 10));
CPPUNIT_ASSERT_EQUAL(true, queue.waitDequeue(item, 10));
CPPUNIT_ASSERT_EQUAL(0UL, queue.size());
const std::vector<std::string> dup_messages =
{
"tile: part=0 width=180 height=135 tileposx=0 tileposy=0 tilewidth=15875 tileheight=11906 ver=-1",
"tile: part=0 width=180 height=135 tileposx=0 tileposy=0 tilewidth=15875 tileheight=11906 ver=1",
"tile: part=0 width=180 height=135 tileposx=0 tileposy=0 tilewidth=15875 tileheight=11906 ver=1"
};
for (const auto& msg : dup_messages)
{
queue.enqueue(std::make_shared<Message>(msg, Message::Dir::Out));
}
CPPUNIT_ASSERT_EQUAL(1UL, queue.size());
CPPUNIT_ASSERT_EQUAL(true, queue.waitDequeue(item, 10));
// The last one should persist.
CPPUNIT_ASSERT_EQUAL(dup_messages[2], std::string(item->data().data(), item->data().size()));
CPPUNIT_ASSERT_EQUAL(0UL, queue.size());
}
void TileQueueTests::testInvalidateViewCursorDeduplication()
{
SenderQueue<std::shared_ptr<Message>> queue;
std::shared_ptr<Message> item;
// Empty queue
CPPUNIT_ASSERT_EQUAL(false, queue.waitDequeue(item, 10));
CPPUNIT_ASSERT_EQUAL(0UL, queue.size());
const std::vector<std::string> view_messages =
{
"invalidateviewcursor: { \"viewId\": \"1\", \"rectangle\": \"3999, 1418, 0, 298\", \"part\": \"0\" }",
"invalidateviewcursor: { \"viewId\": \"2\", \"rectangle\": \"3999, 1418, 0, 298\", \"part\": \"0\" }",
"invalidateviewcursor: { \"viewId\": \"3\", \"rectangle\": \"3999, 1418, 0, 298\", \"part\": \"0\" }",
};
for (const auto& msg : view_messages)
{
queue.enqueue(std::make_shared<Message>(msg, Message::Dir::Out));
}
CPPUNIT_ASSERT_EQUAL(3UL, queue.size());
CPPUNIT_ASSERT_EQUAL(true, queue.waitDequeue(item, 0));
CPPUNIT_ASSERT_EQUAL(2UL, queue.size());
CPPUNIT_ASSERT_EQUAL(view_messages[0], std::string(item->data().data(), item->data().size()));
CPPUNIT_ASSERT_EQUAL(true, queue.waitDequeue(item, 0));
CPPUNIT_ASSERT_EQUAL(1UL, queue.size());
CPPUNIT_ASSERT_EQUAL(view_messages[1], std::string(item->data().data(), item->data().size()));
CPPUNIT_ASSERT_EQUAL(true, queue.waitDequeue(item, 0));
CPPUNIT_ASSERT_EQUAL(0UL, queue.size());
CPPUNIT_ASSERT_EQUAL(view_messages[2], std::string(item->data().data(), item->data().size()));
CPPUNIT_ASSERT_EQUAL(0UL, queue.size());
const std::vector<std::string> dup_messages =
{
"invalidateviewcursor: { \"viewId\": \"1\", \"rectangle\": \"3999, 1418, 0, 298\", \"part\": \"0\" }",
"invalidateviewcursor: { \"viewId\": \"1\", \"rectangle\": \"1000, 1418, 0, 298\", \"part\": \"0\" }",
"invalidateviewcursor: { \"viewId\": \"1\", \"rectangle\": \"2000, 1418, 0, 298\", \"part\": \"0\" }",
};
for (const auto& msg : dup_messages)
{
queue.enqueue(std::make_shared<Message>(msg, Message::Dir::Out));
}
CPPUNIT_ASSERT_EQUAL(1UL, queue.size());
CPPUNIT_ASSERT_EQUAL(true, queue.waitDequeue(item, 0));
// The last one should persist.
CPPUNIT_ASSERT_EQUAL(dup_messages[2], std::string(item->data().data(), item->data().size()));
CPPUNIT_ASSERT_EQUAL(0UL, queue.size());
}
void TileQueueTests::testCallbackInvalidation()
{
TileQueue queue;
// join tiles
queue.put("callback all 0 284, 1418, 11105, 275, 0");
queue.put("callback all 0 4299, 1418, 7090, 275, 0");
CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(queue._queue.size()));
CPPUNIT_ASSERT_EQUAL(std::string("callback all 0 284, 1418, 11105, 275, 0"), payloadAsString(queue.get()));
// invalidate everywhing with EMPTY, but keep the different part intact
queue.put("callback all 0 284, 1418, 11105, 275, 0");
queue.put("callback all 0 4299, 1418, 7090, 275, 1");
queue.put("callback all 0 4299, 10418, 7090, 275, 0");
queue.put("callback all 0 4299, 20418, 7090, 275, 0");
CPPUNIT_ASSERT_EQUAL(4, static_cast<int>(queue._queue.size()));
queue.put("callback all 0 EMPTY, 0");
CPPUNIT_ASSERT_EQUAL(2, static_cast<int>(queue._queue.size()));
CPPUNIT_ASSERT_EQUAL(std::string("callback all 0 4299, 1418, 7090, 275, 1"), payloadAsString(queue.get()));
CPPUNIT_ASSERT_EQUAL(std::string("callback all 0 EMPTY, 0"), payloadAsString(queue.get()));
}
CPPUNIT_TEST_SUITE_REGISTRATION(TileQueueTests);
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */