mirror of https://github.com/qTox/qTox.git
Browse Source
Anthony Bilinski (20): revert(chatlog): "fix stick to bottom behavior" revert(chatlog): partially revert "prevent invalid history access" revert(chatlog): "add comments for functions that load history" revert(chatlog): "scroll bar stuck to bottom (fix #5755)" revert(chatlog): "update workerStb" revert(chatlog): "optimize load messages during the search" revert(chatlog): "feat: save selected search text after scrolling up" revert(chatlog): "feat: check chat status before start a search" revert(chatlog): "fix: data validation during the search" revert(chatlog): "fix a crash when there are no messages to load" revert(chatlog): "prohibition to remove messages in group chat" revert(chatlog): "edit load history when scrolling" revert(chatlog): "simple edit code" revert(chatlog): "remove part messages from chat" revert(chatlog): "edit position chat after load history" revert(chatlog): "add action "Go to current date"" revert(chatlog): "edit load history in search" revert(chatlog): "edit function "Load chat history"" revert(chatlog): "load messages from the database after date" chore(review): Copyright notice cleanup Mick Sayson (8): fix(history): Fix qt deprecation warning revert(chatlog): "feat: load messages from the database before date" revert(chatlog): Revert cleanup refactor(chatlog): Move rendering of messages from GenericChatForm -> ChatLog refactor(chatlog): Store ChatLine::Ptr in messages instead of ChatMessage feat(chatlog): Re-implement sliding window ChatLog view refactor(chatlog): Remove unused getRow functions from ChatLine refactor(chatlog): Rename ChatLog -> ChatWidgetreviewable/pr6413/r1
33 changed files with 2395 additions and 1971 deletions
@ -0,0 +1,222 @@
@@ -0,0 +1,222 @@
|
||||
/*
|
||||
Copyright © 2020-2021 by The qTox Project Contributors |
||||
|
||||
This file is part of qTox, a Qt-based graphical interface for Tox. |
||||
|
||||
qTox is libre software: you can redistribute it and/or modify |
||||
it under the terms of the GNU General Public License as published by |
||||
the Free Software Foundation, either version 3 of the License, or |
||||
(at your option) any later version. |
||||
|
||||
qTox is distributed in the hope that it will be useful, |
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
GNU General Public License for more details. |
||||
|
||||
You should have received a copy of the GNU General Public License |
||||
along with qTox. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/ |
||||
|
||||
#include "chatlinestorage.h" |
||||
|
||||
#include <QDebug> |
||||
|
||||
ChatLineStorage::iterator ChatLineStorage::insertChatMessage(ChatLogIdx idx, QDateTime timestamp, ChatLine::Ptr line) |
||||
{ |
||||
if (idxInfoMap.find(idx) != idxInfoMap.end()) { |
||||
qWarning() << "Index is already rendered, not updating"; |
||||
return lines.end(); |
||||
} |
||||
|
||||
auto linePosIncrementIt = infoIteratorForIdx(idx); |
||||
|
||||
auto insertionPoint = equivalentLineIterator(linePosIncrementIt); |
||||
insertionPoint = adjustItForDate(insertionPoint, timestamp); |
||||
|
||||
insertionPoint = lines.insert(insertionPoint, line); |
||||
|
||||
// All indexes after the insertion have to be incremented by one
|
||||
incrementLinePosAfter(linePosIncrementIt); |
||||
|
||||
// Newly inserted index is insertinPoint - start
|
||||
IdxInfo info; |
||||
info.linePos = std::distance(lines.begin(), insertionPoint); |
||||
info.timestamp = timestamp; |
||||
idxInfoMap[idx] = info; |
||||
|
||||
return insertionPoint; |
||||
} |
||||
|
||||
ChatLineStorage::iterator ChatLineStorage::insertDateLine(QDateTime timestamp, ChatLine::Ptr line) |
||||
{ |
||||
// Assume we only need to render one date line per date. I.e. this does
|
||||
// not handle the case of
|
||||
// * Message inserted Jan 3
|
||||
// * Message inserted Jan 4
|
||||
// * Message inserted Jan 3
|
||||
// In this case the second "Jan 3" message will appear to have been sent
|
||||
// on Jan 4
|
||||
// As of right now this should not be a problem since all items should
|
||||
// be sent/received in order. If we ever implement sender timestamps and
|
||||
// the sender screws us by changing their time we may need to revisit this
|
||||
auto idxMapIt = std::find_if(idxInfoMap.begin(), idxInfoMap.end(), [&] (const IdxInfoMap_t::value_type& v) { |
||||
return timestamp <= v.second.timestamp; |
||||
}); |
||||
|
||||
auto insertionPoint = equivalentLineIterator(idxMapIt); |
||||
insertionPoint = adjustItForDate(insertionPoint, timestamp); |
||||
|
||||
insertionPoint = lines.insert(insertionPoint, line); |
||||
|
||||
// All indexes after the insertion have to be incremented by one
|
||||
incrementLinePosAfter(idxMapIt); |
||||
|
||||
dateMap[line] = timestamp; |
||||
|
||||
return insertionPoint; |
||||
} |
||||
|
||||
bool ChatLineStorage::contains(QDateTime timestamp) const |
||||
{ |
||||
auto it = std::find_if(dateMap.begin(), dateMap.end(), [&] (DateLineMap_t::value_type v) { |
||||
return v.second == timestamp; |
||||
}); |
||||
|
||||
return it != dateMap.end(); |
||||
} |
||||
|
||||
ChatLineStorage::iterator ChatLineStorage::find(ChatLogIdx idx) |
||||
{ |
||||
auto infoIt = infoIteratorForIdx(idx); |
||||
if (infoIt == idxInfoMap.end()) { |
||||
return lines.end(); |
||||
} |
||||
|
||||
return lines.begin() + infoIt->second.linePos; |
||||
|
||||
} |
||||
|
||||
ChatLineStorage::iterator ChatLineStorage::find(ChatLine::Ptr line) |
||||
{ |
||||
return std::find(lines.begin(), lines.end(), line); |
||||
} |
||||
|
||||
void ChatLineStorage::erase(ChatLogIdx idx) |
||||
{ |
||||
auto linePosDecrementIt = infoIteratorForIdx(idx); |
||||
auto lineIt = equivalentLineIterator(linePosDecrementIt); |
||||
|
||||
erase(lineIt); |
||||
} |
||||
|
||||
ChatLineStorage::iterator ChatLineStorage::erase(iterator it) |
||||
{ |
||||
iterator prevIt = it; |
||||
|
||||
do { |
||||
it = prevIt; |
||||
|
||||
auto infoIterator = equivalentInfoIterator(it); |
||||
auto dateMapIt = dateMap.find(*it); |
||||
|
||||
if (dateMapIt != dateMap.end()) { |
||||
dateMap.erase(dateMapIt); |
||||
} |
||||
|
||||
if (infoIterator != idxInfoMap.end()) { |
||||
infoIterator = idxInfoMap.erase(infoIterator); |
||||
decrementLinePosAfter(infoIterator); |
||||
} |
||||
|
||||
it = lines.erase(it); |
||||
|
||||
if (it > lines.begin()) { |
||||
prevIt = std::prev(it); |
||||
} else { |
||||
prevIt = lines.end(); |
||||
} |
||||
} while (shouldRemovePreviousLine(prevIt, it)); |
||||
|
||||
return it; |
||||
} |
||||
|
||||
ChatLineStorage::iterator ChatLineStorage::equivalentLineIterator(IdxInfoMap_t::iterator it) |
||||
{ |
||||
if (it == idxInfoMap.end()) { |
||||
return lines.end(); |
||||
} |
||||
|
||||
return std::next(lines.begin(), it->second.linePos); |
||||
} |
||||
|
||||
ChatLineStorage::IdxInfoMap_t::iterator ChatLineStorage::equivalentInfoIterator(iterator it) |
||||
{ |
||||
auto idx = static_cast<size_t>(std::distance(lines.begin(), it)); |
||||
auto equivalentIt = std::find_if(idxInfoMap.begin(), idxInfoMap.end(), [&](const IdxInfoMap_t::value_type& v) { |
||||
return v.second.linePos >= idx; |
||||
}); |
||||
|
||||
return equivalentIt; |
||||
} |
||||
|
||||
ChatLineStorage::IdxInfoMap_t::iterator ChatLineStorage::infoIteratorForIdx(ChatLogIdx idx) |
||||
{ |
||||
// If lower_bound proves to be expensive for appending we can try
|
||||
// special casing when idx > idxToLineMap.rbegin()->first
|
||||
|
||||
// If we find an exact match we return that index, otherwise we return
|
||||
// the first item after it. It's up to the caller to check if there's an
|
||||
// exact match first
|
||||
auto it = std::lower_bound(idxInfoMap.begin(), idxInfoMap.end(), idx, [](const IdxInfoMap_t::value_type& v, ChatLogIdx idx) { |
||||
return v.first < idx; |
||||
}); |
||||
|
||||
return it; |
||||
} |
||||
|
||||
ChatLineStorage::iterator ChatLineStorage::adjustItForDate(iterator it, QDateTime timestamp) |
||||
{ |
||||
// Continuously move back until either
|
||||
// 1. The dateline found is earlier than our timestamp
|
||||
// 2. There are no more datelines
|
||||
while (it > lines.begin()) { |
||||
auto possibleDateIt = it - 1; |
||||
auto dateIt = dateMap.find(*possibleDateIt); |
||||
if (dateIt == dateMap.end()) { |
||||
break; |
||||
} |
||||
|
||||
if (dateIt->second > timestamp) { |
||||
it = possibleDateIt; |
||||
} else { |
||||
break; |
||||
} |
||||
} |
||||
|
||||
return it; |
||||
} |
||||
|
||||
void ChatLineStorage::incrementLinePosAfter(IdxInfoMap_t::iterator inputIt) |
||||
{ |
||||
for (auto it = inputIt; it != idxInfoMap.end(); ++it) { |
||||
it->second.linePos++; |
||||
} |
||||
} |
||||
|
||||
void ChatLineStorage::decrementLinePosAfter(IdxInfoMap_t::iterator inputIt) |
||||
{ |
||||
// All indexes after the insertion have to be incremented by one
|
||||
for (auto it = inputIt; it != idxInfoMap.end(); ++it) { |
||||
it->second.linePos--; |
||||
} |
||||
} |
||||
|
||||
bool ChatLineStorage::shouldRemovePreviousLine(iterator prevIt, iterator it) |
||||
{ |
||||
return prevIt != lines.end() && // Previous iterator is valid
|
||||
dateMap.find(*prevIt) != dateMap.end() && // Previous iterator is a date line
|
||||
( |
||||
it == lines.end() || // Previous iterator is the last line
|
||||
dateMap.find(*it) != dateMap.end() // Adjacent date lines
|
||||
); |
||||
} |
@ -0,0 +1,131 @@
@@ -0,0 +1,131 @@
|
||||
/*
|
||||
Copyright © 2021 by The qTox Project Contributors |
||||
|
||||
This file is part of qTox, a Qt-based graphical interface for Tox. |
||||
|
||||
qTox is libre software: you can redistribute it and/or modify |
||||
it under the terms of the GNU General Public License as published by |
||||
the Free Software Foundation, either version 3 of the License, or |
||||
(at your option) any later version. |
||||
|
||||
qTox is distributed in the hope that it will be useful, |
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
GNU General Public License for more details. |
||||
|
||||
You should have received a copy of the GNU General Public License |
||||
along with qTox. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/ |
||||
|
||||
#pragma once |
||||
|
||||
#include "src/chatlog/chatline.h" |
||||
#include "src/model/ichatlog.h" |
||||
|
||||
#include <QDateTime> |
||||
|
||||
#include <vector> |
||||
#include <map> |
||||
|
||||
/**
|
||||
* Helper class to keep track of what we're currently rendering and in what order |
||||
* Some constraints that may not be obvious |
||||
* * Rendered views are not always contiguous. When we clear the chatlog |
||||
* ongoing file transfers are not removed or else we would have no way to stop |
||||
* them. If history is loaded after this point then we could actually be |
||||
* inserting elements both before and in the middle of our existing rendered |
||||
* items |
||||
* * We need to be able to go from ChatLogIdx to rendered row index. E.g. if |
||||
* an SQL query is made for search, we need to map the result back to the |
||||
* displayed row to send it |
||||
* * We need to be able to map rows back to ChatLogIdx in order to decide where |
||||
* to insert newly added messages |
||||
* * Not all rendered lines will have an associated ChatLogIdx, date lines for |
||||
* example are not clearly at any ChatLogIdx |
||||
* * Need to track date messages to ensure that if messages are inserted above |
||||
* the current position the date line is moved appropriately |
||||
* |
||||
* The class is designed to be used like a vector over the currently rendered |
||||
* items, but with some tweaks for ensuring items tied to the current view are |
||||
* moved correctly (selection indexes, removal of associated date lines, |
||||
* mappings of ChatLogIdx -> ChatLine::Ptr, etc.) |
||||
*/ |
||||
class ChatLineStorage |
||||
{ |
||||
|
||||
struct IdxInfo |
||||
{ |
||||
size_t linePos; |
||||
QDateTime timestamp; |
||||
}; |
||||
using Lines_t = std::vector<ChatLine::Ptr>; |
||||
using DateLineMap_t = std::map<ChatLine::Ptr, QDateTime>; |
||||
using IdxInfoMap_t = std::map<ChatLogIdx, IdxInfo>; |
||||
|
||||
public: |
||||
// Types to conform with other containers
|
||||
using size_type = Lines_t::size_type; |
||||
using reference = Lines_t::reference; |
||||
using const_reference = Lines_t::const_reference; |
||||
using const_iterator = Lines_t::const_iterator; |
||||
using iterator = Lines_t::iterator; |
||||
|
||||
|
||||
public: |
||||
iterator insertChatMessage(ChatLogIdx idx, QDateTime timestamp, ChatLine::Ptr line); |
||||
iterator insertDateLine(QDateTime timestamp, ChatLine::Ptr line); |
||||
|
||||
ChatLogIdx firstIdx() const { return idxInfoMap.begin()->first; } |
||||
|
||||
ChatLogIdx lastIdx() const { return idxInfoMap.rbegin()->first; } |
||||
|
||||
bool contains(ChatLogIdx idx) const { return idxInfoMap.find(idx) != idxInfoMap.end(); } |
||||
|
||||
bool contains(QDateTime timestamp) const; |
||||
|
||||
iterator find(ChatLogIdx idx); |
||||
iterator find(ChatLine::Ptr line); |
||||
|
||||
const_reference operator[](size_type idx) const { return lines[idx]; } |
||||
|
||||
const_reference operator[](ChatLogIdx idx) const { return lines[idxInfoMap.at(idx).linePos]; } |
||||
|
||||
size_type size() const { return lines.size(); } |
||||
|
||||
iterator begin() { return lines.begin(); } |
||||
iterator end() { return lines.end(); } |
||||
|
||||
bool empty() const { return lines.empty(); } |
||||
|
||||
bool hasIndexedMessage() const { return !idxInfoMap.empty(); } |
||||
|
||||
void clear() |
||||
{ |
||||
idxInfoMap.clear(); |
||||
dateMap.clear(); |
||||
return lines.clear(); |
||||
} |
||||
|
||||
reference front() { return lines.front(); } |
||||
reference back() { return lines.back(); } |
||||
|
||||
void erase(ChatLogIdx idx); |
||||
iterator erase(iterator it); |
||||
|
||||
private: |
||||
iterator equivalentLineIterator(IdxInfoMap_t::iterator it); |
||||
|
||||
IdxInfoMap_t::iterator equivalentInfoIterator(iterator it); |
||||
|
||||
IdxInfoMap_t::iterator infoIteratorForIdx(ChatLogIdx idx); |
||||
|
||||
iterator adjustItForDate(iterator it, QDateTime timestamp); |
||||
|
||||
void incrementLinePosAfter(IdxInfoMap_t::iterator it); |
||||
void decrementLinePosAfter(IdxInfoMap_t::iterator it); |
||||
bool shouldRemovePreviousLine(iterator prevIt, iterator it); |
||||
|
||||
std::vector<ChatLine::Ptr> lines; |
||||
std::map<ChatLine::Ptr, QDateTime> dateMap; |
||||
IdxInfoMap_t idxInfoMap; |
||||
}; |
@ -0,0 +1,353 @@
@@ -0,0 +1,353 @@
|
||||
/*
|
||||
Copyright © 2021 by The qTox Project Contributors |
||||
|
||||
This file is part of qTox, a Qt-based graphical interface for Tox. |
||||
|
||||
qTox is libre software: you can redistribute it and/or modify |
||||
it under the terms of the GNU General Public License as published by |
||||
the Free Software Foundation, either version 3 of the License, or |
||||
(at your option) any later version. |
||||
|
||||
qTox is distributed in the hope that it will be useful, |
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
GNU General Public License for more details. |
||||
|
||||
You should have received a copy of the GNU General Public License |
||||
along with qTox. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/ |
||||
|
||||
#include "src/chatlog/chatlinestorage.h" |
||||
#include <QTest> |
||||
|
||||
namespace |
||||
{ |
||||
class IdxChatLine : public ChatLine |
||||
{ |
||||
public: |
||||
explicit IdxChatLine(ChatLogIdx idx) |
||||
: ChatLine() |
||||
, idx(idx) |
||||
{} |
||||
|
||||
ChatLogIdx get() { return idx; } |
||||
private: |
||||
ChatLogIdx idx; |
||||
|
||||
}; |
||||
|
||||
class TimestampChatLine : public ChatLine |
||||
{ |
||||
public: |
||||
explicit TimestampChatLine(QDateTime dateTime) |
||||
: ChatLine() |
||||
, timestamp(dateTime) |
||||
{} |
||||
|
||||
QDateTime get() { return timestamp; } |
||||
private: |
||||
QDateTime timestamp; |
||||
}; |
||||
|
||||
ChatLogIdx idxFromChatLine(ChatLine::Ptr p) { |
||||
return std::static_pointer_cast<IdxChatLine>(p)->get(); |
||||
} |
||||
|
||||
QDateTime timestampFromChatLine(ChatLine::Ptr p) { |
||||
return std::static_pointer_cast<TimestampChatLine>(p)->get(); |
||||
} |
||||
|
||||
} // namespace
|
||||
|
||||
|
||||
class TestChatLineStorage : public QObject |
||||
{ |
||||
Q_OBJECT |
||||
|
||||
private slots: |
||||
void init(); |
||||
void testChatLogIdxAccess(); |
||||
void testIndexAccess(); |
||||
void testRangeBasedIteration(); |
||||
void testAppendingItems(); |
||||
void testPrependingItems(); |
||||
void testMiddleInsertion(); |
||||
void testIndexRemoval(); |
||||
void testItRemoval(); |
||||
void testDateLineAddition(); |
||||
void testDateLineRemoval(); |
||||
void testInsertionBeforeDates(); |
||||
void testInsertionAfterDate(); |
||||
void testContainsTimestamp(); |
||||
void testContainsIdx(); |
||||
void testEndOfStorageDateRemoval(); |
||||
void testConsecutiveDateLineRemoval(); |
||||
private: |
||||
ChatLineStorage storage; |
||||
|
||||
static constexpr size_t initialStartIdx = 10; |
||||
static constexpr size_t initialEndIdx = 20; |
||||
static const QDateTime initialTimestamp; |
||||
|
||||
}; |
||||
|
||||
constexpr size_t TestChatLineStorage::initialStartIdx; |
||||
constexpr size_t TestChatLineStorage::initialEndIdx; |
||||
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)) |
||||
const QDateTime TestChatLineStorage::initialTimestamp = QDate(2021, 01, 01).startOfDay(); |
||||
#else |
||||
const QDateTime TestChatLineStorage::initialTimestamp(QDate(2021, 01, 01)); |
||||
#endif |
||||
|
||||
void TestChatLineStorage::init() |
||||
{ |
||||
storage = ChatLineStorage(); |
||||
|
||||
for (auto idx = ChatLogIdx(initialStartIdx); idx < ChatLogIdx(initialEndIdx); ++idx) { |
||||
storage.insertChatMessage(idx, initialTimestamp, std::make_shared<IdxChatLine>(idx)); |
||||
} |
||||
} |
||||
|
||||
void TestChatLineStorage::testChatLogIdxAccess() |
||||
{ |
||||
for (auto idx = ChatLogIdx(initialStartIdx); idx < ChatLogIdx(initialEndIdx); ++idx) { |
||||
QCOMPARE(idxFromChatLine(storage[idx]).get(), idx.get()); |
||||
} |
||||
} |
||||
|
||||
void TestChatLineStorage::testIndexAccess() |
||||
{ |
||||
for (size_t i = 0; i < initialEndIdx - initialStartIdx; ++i) { |
||||
QCOMPARE(idxFromChatLine(storage[i]).get(), initialStartIdx + i); |
||||
} |
||||
} |
||||
|
||||
|
||||
void TestChatLineStorage::testRangeBasedIteration() |
||||
{ |
||||
auto idx = ChatLogIdx(initialStartIdx); |
||||
|
||||
for (const auto& p : storage) { |
||||
QCOMPARE(idxFromChatLine(p).get(), idx.get()); |
||||
idx = idx + 1; |
||||
} |
||||
} |
||||
|
||||
void TestChatLineStorage::testAppendingItems() |
||||
{ |
||||
for (auto idx = ChatLogIdx(initialEndIdx); idx < ChatLogIdx(initialEndIdx + 10); ++idx) { |
||||
storage.insertChatMessage(idx, initialTimestamp, std::make_shared<IdxChatLine>(idx)); |
||||
QCOMPARE(storage.lastIdx().get(), idx.get()); |
||||
} |
||||
|
||||
for (auto idx = ChatLogIdx(initialEndIdx); idx < storage.lastIdx(); ++idx) { |
||||
QCOMPARE(idxFromChatLine(storage[idx]).get(), idx.get()); |
||||
QCOMPARE(idxFromChatLine(storage[idx.get() - initialStartIdx]).get(), idx.get()); |
||||
} |
||||
} |
||||
|
||||
void TestChatLineStorage::testMiddleInsertion() |
||||
{ |
||||
ChatLogIdx newEnd = ChatLogIdx(initialEndIdx + 5); |
||||
ChatLogIdx insertIdx = ChatLogIdx(initialEndIdx + 3); |
||||
|
||||
storage.insertChatMessage(newEnd, initialTimestamp, std::make_shared<IdxChatLine>(newEnd)); |
||||
storage.insertChatMessage(insertIdx, initialTimestamp, std::make_shared<IdxChatLine>(insertIdx)); |
||||
|
||||
QCOMPARE(idxFromChatLine(storage[insertIdx]).get(), insertIdx.get()); |
||||
QCOMPARE(idxFromChatLine(storage[initialEndIdx - initialStartIdx]).get(), insertIdx.get()); |
||||
QCOMPARE(idxFromChatLine(storage[initialEndIdx - initialStartIdx + 1]).get(), newEnd.get()); |
||||
} |
||||
|
||||
void TestChatLineStorage::testPrependingItems() |
||||
{ |
||||
for (auto idx = ChatLogIdx(initialStartIdx - 1); idx != ChatLogIdx(-1); idx = idx - 1) { |
||||
storage.insertChatMessage(idx, initialTimestamp, std::make_shared<IdxChatLine>(idx)); |
||||
QCOMPARE(storage.firstIdx().get(), idx.get()); |
||||
} |
||||
|
||||
for (auto idx = storage.firstIdx(); idx < storage.lastIdx(); ++idx) { |
||||
QCOMPARE(idxFromChatLine(storage[idx]).get(), idx.get()); |
||||
QCOMPARE(idxFromChatLine(storage[idx.get()]).get(), idx.get()); |
||||
} |
||||
} |
||||
|
||||
void TestChatLineStorage::testIndexRemoval() |
||||
{ |
||||
QCOMPARE(initialStartIdx, static_cast<size_t>(10)); |
||||
QCOMPARE(initialEndIdx, static_cast<size_t>(20)); |
||||
QCOMPARE(storage.size(), static_cast<size_t>(10)); |
||||
|
||||
storage.erase(ChatLogIdx(11)); |
||||
|
||||
QCOMPARE(storage.size(), static_cast<size_t>(9)); |
||||
|
||||
QCOMPARE(idxFromChatLine(storage[0]).get(), static_cast<size_t>(10)); |
||||
QCOMPARE(idxFromChatLine(storage[1]).get(), static_cast<size_t>(12)); |
||||
|
||||
auto idx = static_cast<size_t>(12); |
||||
for (auto it = std::next(storage.begin()); it != storage.end(); ++it) { |
||||
QCOMPARE(idxFromChatLine((*it)).get(), idx++); |
||||
} |
||||
} |
||||
|
||||
void TestChatLineStorage::testItRemoval() |
||||
{ |
||||
auto it = storage.begin(); |
||||
it = it + 2; |
||||
|
||||
storage.erase(it); |
||||
|
||||
QCOMPARE(idxFromChatLine(storage[0]).get(), initialStartIdx); |
||||
QCOMPARE(idxFromChatLine(storage[1]).get(), initialStartIdx + 1); |
||||
// Item should have been removed
|
||||
QCOMPARE(idxFromChatLine(storage[2]).get(), initialStartIdx + 3); |
||||
} |
||||
|
||||
void TestChatLineStorage::testDateLineAddition() |
||||
{ |
||||
storage.insertDateLine(initialTimestamp, std::make_shared<TimestampChatLine>(initialTimestamp)); |
||||
auto newTimestamp = initialTimestamp.addDays(1); |
||||
storage.insertDateLine(newTimestamp, std::make_shared<TimestampChatLine>(newTimestamp)); |
||||
|
||||
QCOMPARE(storage.size(), initialEndIdx - initialStartIdx + 2); |
||||
QCOMPARE(timestampFromChatLine(storage[0]), initialTimestamp); |
||||
QCOMPARE(timestampFromChatLine(storage[storage.size() - 1]), newTimestamp); |
||||
|
||||
for (size_t i = 1; i < storage.size() - 2; ++i) |
||||
{ |
||||
// Ensure that indexed items all stayed in the right order
|
||||
QCOMPARE(idxFromChatLine(storage[i]).get(), idxFromChatLine(storage[ChatLogIdx(initialStartIdx + i - 1)]).get()); |
||||
} |
||||
|
||||
} |
||||
|
||||
void TestChatLineStorage::testDateLineRemoval() |
||||
{ |
||||
// For the time being there is no removal requirement
|
||||
storage.insertDateLine(initialTimestamp, std::make_shared<TimestampChatLine>(initialTimestamp)); |
||||
|
||||
QVERIFY(storage.contains(initialTimestamp)); |
||||
QCOMPARE(timestampFromChatLine(storage[0]), initialTimestamp); |
||||
|
||||
storage.erase(storage.begin()); |
||||
|
||||
QVERIFY(!storage.contains(initialTimestamp)); |
||||
QCOMPARE(idxFromChatLine(storage[0]).get(), initialStartIdx); |
||||
} |
||||
|
||||
void TestChatLineStorage::testInsertionBeforeDates() |
||||
{ |
||||
storage.insertDateLine(initialTimestamp, std::make_shared<TimestampChatLine>(initialTimestamp)); |
||||
|
||||
auto yesterday = initialTimestamp.addDays(-1); |
||||
storage.insertDateLine(yesterday, std::make_shared<TimestampChatLine>(yesterday)); |
||||
|
||||
auto firstIdx = ChatLogIdx(initialStartIdx - 2); |
||||
storage.insertChatMessage(firstIdx, initialTimestamp.addDays(-2), std::make_shared<IdxChatLine>(firstIdx)); |
||||
|
||||
QCOMPARE(idxFromChatLine(storage[0]).get(), firstIdx.get()); |
||||
QCOMPARE(timestampFromChatLine(storage[1]), yesterday); |
||||
QCOMPARE(timestampFromChatLine(storage[2]), initialTimestamp); |
||||
QCOMPARE(idxFromChatLine(storage[3]).get(), initialStartIdx); |
||||
|
||||
auto secondIdx = ChatLogIdx(initialStartIdx - 1); |
||||
storage.insertChatMessage(secondIdx, initialTimestamp.addDays(-1), std::make_shared<IdxChatLine>(secondIdx)); |
||||
|
||||
QCOMPARE(idxFromChatLine(storage[0]).get(), firstIdx.get()); |
||||
QCOMPARE(timestampFromChatLine(storage[1]), yesterday); |
||||
QCOMPARE(idxFromChatLine(storage[2]).get(), secondIdx.get()); |
||||
QCOMPARE(timestampFromChatLine(storage[3]), initialTimestamp); |
||||
QCOMPARE(idxFromChatLine(storage[4]).get(), initialStartIdx); |
||||
} |
||||
|
||||
void TestChatLineStorage::testInsertionAfterDate() |
||||
{ |
||||
auto newTimestamp = initialTimestamp.addDays(1); |
||||
storage.insertDateLine(newTimestamp, std::make_shared<TimestampChatLine>(newTimestamp)); |
||||
|
||||
QCOMPARE(storage.size(), initialEndIdx - initialStartIdx + 1); |
||||
QCOMPARE(timestampFromChatLine(storage[initialEndIdx - initialStartIdx]), newTimestamp); |
||||
|
||||
storage.insertChatMessage(ChatLogIdx(initialEndIdx), newTimestamp, std::make_shared<IdxChatLine>(ChatLogIdx(initialEndIdx))); |
||||
QCOMPARE(idxFromChatLine(storage[initialEndIdx - initialStartIdx + 1]).get(), initialEndIdx); |
||||
QCOMPARE(idxFromChatLine(storage[ChatLogIdx(initialEndIdx)]).get(), initialEndIdx); |
||||
} |
||||
|
||||
void TestChatLineStorage::testContainsTimestamp() |
||||
{ |
||||
QCOMPARE(storage.contains(initialTimestamp), false); |
||||
storage.insertDateLine(initialTimestamp, std::make_shared<TimestampChatLine>(initialTimestamp)); |
||||
QCOMPARE(storage.contains(initialTimestamp), true); |
||||
} |
||||
|
||||
void TestChatLineStorage::testContainsIdx() |
||||
{ |
||||
QCOMPARE(storage.contains(ChatLogIdx(initialEndIdx)), false); |
||||
QCOMPARE(storage.contains(ChatLogIdx(initialStartIdx)), true); |
||||
} |
||||
|
||||
void TestChatLineStorage::testEndOfStorageDateRemoval() |
||||
{ |
||||
auto tomorrow = initialTimestamp.addDays(1); |
||||
storage.insertDateLine(tomorrow, std::make_shared<TimestampChatLine>(tomorrow)); |
||||
storage.insertChatMessage(ChatLogIdx(initialEndIdx), tomorrow, std::make_shared<IdxChatLine>(ChatLogIdx(initialEndIdx))); |
||||
|
||||
QCOMPARE(storage.size(), initialEndIdx - initialStartIdx + 2); |
||||
|
||||
auto it = storage.begin() + storage.size() - 2; |
||||
QCOMPARE(timestampFromChatLine(*it++), tomorrow); |
||||
QCOMPARE(idxFromChatLine(*it).get(), initialEndIdx); |
||||
|
||||
storage.erase(it); |
||||
|
||||
QCOMPARE(storage.size(), initialEndIdx - initialStartIdx); |
||||
|
||||
it = storage.begin() + storage.size() - 1; |
||||
QCOMPARE(idxFromChatLine(*it++).get(), initialEndIdx - 1); |
||||
} |
||||
|
||||
void TestChatLineStorage::testConsecutiveDateLineRemoval() |
||||
{ |
||||
auto todayPlus1 = initialTimestamp.addDays(1); |
||||
auto todayPlus2 = initialTimestamp.addDays(2); |
||||
|
||||
auto todayPlus1Idx = ChatLogIdx(initialEndIdx); |
||||
auto todayPlus1Idx2 = ChatLogIdx(initialEndIdx + 1); |
||||
auto todayPlus2Idx = ChatLogIdx(initialEndIdx + 2); |
||||
|
||||
|
||||
storage.insertDateLine(todayPlus1, std::make_shared<TimestampChatLine>(todayPlus1)); |
||||
storage.insertChatMessage(todayPlus1Idx, todayPlus1, std::make_shared<IdxChatLine>(todayPlus1Idx)); |
||||
storage.insertChatMessage(todayPlus1Idx2, todayPlus1, std::make_shared<IdxChatLine>(todayPlus1Idx2)); |
||||
|
||||
storage.insertDateLine(todayPlus2, std::make_shared<TimestampChatLine>(todayPlus2)); |
||||
storage.insertChatMessage(todayPlus2Idx, todayPlus2, std::make_shared<IdxChatLine>(todayPlus2Idx)); |
||||
|
||||
// 2 date lines and 3 messages were inserted for a total of 5 new lines
|
||||
QCOMPARE(storage.size(), initialEndIdx - initialStartIdx + 5); |
||||
|
||||
storage.erase(storage.find(todayPlus1Idx2)); |
||||
|
||||
auto newItemIdxStart = initialEndIdx - initialStartIdx; |
||||
|
||||
// Only the chat message should have been removed
|
||||
QCOMPARE(storage.size(), initialEndIdx - initialStartIdx + 4); |
||||
|
||||
QCOMPARE(timestampFromChatLine(storage[newItemIdxStart]), todayPlus1); |
||||
QCOMPARE(idxFromChatLine(storage[newItemIdxStart + 1]).get(), todayPlus1Idx.get()); |
||||
QCOMPARE(timestampFromChatLine(storage[newItemIdxStart + 2]), todayPlus2); |
||||
QCOMPARE(idxFromChatLine(storage[newItemIdxStart + 3]).get(), todayPlus2Idx.get()); |
||||
|
||||
storage.erase(storage.find(todayPlus1Idx)); |
||||
|
||||
// The chat message + the dateline for it should have been removed as there
|
||||
// were 2 adjacent datelines caused by the removal
|
||||
QCOMPARE(storage.size(), initialEndIdx - initialStartIdx + 2); |
||||
QCOMPARE(timestampFromChatLine(storage[newItemIdxStart]), todayPlus2); |
||||
QCOMPARE(idxFromChatLine(storage[newItemIdxStart + 1]).get(), todayPlus2Idx.get()); |
||||
} |
||||
|
||||
QTEST_GUILESS_MAIN(TestChatLineStorage); |
||||
#include "chatlinestorage_test.moc" |
Loading…
Reference in new issue