Add more tests for QList/QString/QBA
The major part is stability tests for QList operations, Also added std::shared_ptr to the Custom type. shared_ptr accesses the memory which does not directly belong to QList, so using it inside a passed-to-qlist type is beneficial (e.g. ASan could catch extra issues) Basic prepend-aware cases added to QString/QBA tests Task-number: QTBUG-93019 Change-Id: I50e742bdf10ea9de2de66539a7dbb9abc4352f82 Reviewed-by: Lars Knoll <lars.knoll@qt.io> (cherry picked from commit adb41bbe00b2b853d4dd26cd9ee77ae5ed541576) Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
This commit is contained in:
parent
dbb001eb1b
commit
e6f7202e34
@ -895,6 +895,27 @@ void tst_QByteArray::append()
|
||||
QByteArray twoChars("ab");
|
||||
tenChars.append(twoChars);
|
||||
QCOMPARE(tenChars.capacity(), 10);
|
||||
|
||||
{
|
||||
QByteArray prepended("abcd");
|
||||
prepended.prepend('a');
|
||||
const qsizetype freeAtEnd = prepended.data_ptr()->freeSpaceAtEnd();
|
||||
QVERIFY(prepended.size() + freeAtEnd < prepended.capacity());
|
||||
prepended += QByteArray(freeAtEnd, 'b');
|
||||
prepended.append('c');
|
||||
QCOMPARE(prepended, QByteArray("aabcd") + QByteArray(freeAtEnd, 'b') + QByteArray("c"));
|
||||
}
|
||||
|
||||
{
|
||||
QByteArray prepended2("aaaaaaaaaa");
|
||||
while (prepended2.size())
|
||||
prepended2.remove(0, 1);
|
||||
QVERIFY(prepended2.data_ptr()->freeSpaceAtBegin() > 0);
|
||||
QByteArray array(prepended2.data_ptr()->freeSpaceAtEnd(), 'a');
|
||||
prepended2 += array;
|
||||
prepended2.append('b');
|
||||
QCOMPARE(prepended2, array + QByteArray("b"));
|
||||
}
|
||||
}
|
||||
|
||||
void tst_QByteArray::appendExtended_data()
|
||||
@ -959,6 +980,38 @@ void tst_QByteArray::insert()
|
||||
ba = "one";
|
||||
QCOMPARE(ba.insert(1, QByteArrayView(ba)), QByteArray("oonene"));
|
||||
QCOMPARE(ba.size(), 6);
|
||||
|
||||
{
|
||||
ba = "one";
|
||||
ba.prepend('a');
|
||||
QByteArray b(ba.data_ptr()->freeSpaceAtEnd(), 'b');
|
||||
QCOMPARE(ba.insert(ba.size() + 1, QByteArrayView(b)), QByteArray("aone ") + b);
|
||||
}
|
||||
|
||||
{
|
||||
ba = "onetwothree";
|
||||
while (ba.size() - 1)
|
||||
ba.remove(0, 1);
|
||||
QByteArray b(ba.data_ptr()->freeSpaceAtEnd() + 1, 'b');
|
||||
QCOMPARE(ba.insert(ba.size() + 1, QByteArrayView(b)), QByteArray("e ") + b);
|
||||
}
|
||||
|
||||
{
|
||||
ba = "one";
|
||||
ba.prepend('a');
|
||||
const qsizetype freeAtEnd = ba.data_ptr()->freeSpaceAtEnd();
|
||||
QCOMPARE(ba.insert(ba.size() + 1, freeAtEnd + 1, 'b'),
|
||||
QByteArray("aone ") + QByteArray(freeAtEnd + 1, 'b'));
|
||||
}
|
||||
|
||||
{
|
||||
ba = "onetwothree";
|
||||
while (ba.size() - 1)
|
||||
ba.remove(0, 1);
|
||||
const qsizetype freeAtEnd = ba.data_ptr()->freeSpaceAtEnd();
|
||||
QCOMPARE(ba.insert(ba.size() + 1, freeAtEnd + 1, 'b'),
|
||||
QByteArray("e ") + QByteArray(freeAtEnd + 1, 'b'));
|
||||
}
|
||||
}
|
||||
|
||||
void tst_QByteArray::insertExtended_data()
|
||||
|
@ -2418,6 +2418,36 @@ void tst_QString::insert_special_cases()
|
||||
QCOMPARE(a.insert(1, QLatin1String("BCD")), QString("ABCDEF"));
|
||||
QCOMPARE(a.insert(3, QLatin1String("-")), QString("ABC-DEF"));
|
||||
QCOMPARE(a.insert(a.size() + 1, QLatin1String("XYZ")), QString("ABC-DEF XYZ"));
|
||||
|
||||
{
|
||||
a = "one";
|
||||
a.prepend(u'a');
|
||||
QString b(a.data_ptr()->freeSpaceAtEnd(), u'b');
|
||||
QCOMPARE(a.insert(a.size() + 1, QLatin1String(b.toLatin1())), QString("aone ") + b);
|
||||
}
|
||||
|
||||
{
|
||||
a = "onetwothree";
|
||||
while (a.size() - 1)
|
||||
a.remove(0, 1);
|
||||
QString b(a.data_ptr()->freeSpaceAtEnd() + 1, u'b');
|
||||
QCOMPARE(a.insert(a.size() + 1, QLatin1String(b.toLatin1())), QString("e ") + b);
|
||||
}
|
||||
|
||||
{
|
||||
a = "one";
|
||||
a.prepend(u'a');
|
||||
QString b(a.data_ptr()->freeSpaceAtEnd(), u'b');
|
||||
QCOMPARE(a.insert(a.size() + 1, b), QString("aone ") + b);
|
||||
}
|
||||
|
||||
{
|
||||
a = "onetwothree";
|
||||
while (a.size() - 1)
|
||||
a.remove(0, 1);
|
||||
QString b(a.data_ptr()->freeSpaceAtEnd() + 1, u'b');
|
||||
QCOMPARE(a.insert(a.size() + 1, b), QString("e ") + b);
|
||||
}
|
||||
}
|
||||
|
||||
void tst_QString::append_data(bool emptyIsNoop)
|
||||
@ -2477,6 +2507,49 @@ void tst_QString::append_special_cases()
|
||||
a.append(QLatin1String("BC"));
|
||||
QCOMPARE(a, QLatin1String("ABC"));
|
||||
}
|
||||
|
||||
{
|
||||
QString a = "one";
|
||||
a.prepend(u'a');
|
||||
QString b(a.data_ptr()->freeSpaceAtEnd(), u'b');
|
||||
QCOMPARE(a.append(QLatin1String(b.toLatin1())), QString("aone") + b);
|
||||
}
|
||||
|
||||
{
|
||||
QString a = "onetwothree";
|
||||
while (a.size() - 1)
|
||||
a.remove(0, 1);
|
||||
QString b(a.data_ptr()->freeSpaceAtEnd(), u'b');
|
||||
QCOMPARE(a.append(QLatin1String(b.toLatin1())), QString("e") + b);
|
||||
}
|
||||
|
||||
{
|
||||
QString a = "one";
|
||||
a.prepend(u'a');
|
||||
QString b(a.data_ptr()->freeSpaceAtEnd(), u'b');
|
||||
QCOMPARE(a.append(b), QString("aone") + b);
|
||||
}
|
||||
|
||||
{
|
||||
QString a = "onetwothree";
|
||||
while (a.size() - 1)
|
||||
a.remove(0, 1);
|
||||
QString b(a.data_ptr()->freeSpaceAtEnd() + 1, u'b');
|
||||
QCOMPARE(a.append(b), QString("e") + b);
|
||||
}
|
||||
|
||||
{
|
||||
QString a = "one";
|
||||
a.prepend(u'a');
|
||||
QCOMPARE(a.append(u'b'), QString("aoneb"));
|
||||
}
|
||||
|
||||
{
|
||||
QString a = "onetwothree";
|
||||
while (a.size() - 1)
|
||||
a.remove(0, 1);
|
||||
QCOMPARE(a.append(u'b'), QString("eb"));
|
||||
}
|
||||
}
|
||||
|
||||
void tst_QString::append_bytearray_special_cases_data()
|
||||
|
@ -159,6 +159,7 @@ struct Custom {
|
||||
i = 0;
|
||||
counter.fetchAndAddRelaxed(-1);
|
||||
state = Destructed;
|
||||
QVERIFY(heapData.use_count() > 0); // otherwise it's double free
|
||||
}
|
||||
|
||||
bool operator ==(const Custom &other) const
|
||||
@ -187,6 +188,9 @@ struct Custom {
|
||||
char i; // used to identify orgin of an instance
|
||||
private:
|
||||
Custom *that; // used to check if an instance was moved
|
||||
// shared_ptr triggers ASan/LSan and can track if double free happens, which
|
||||
// is convenient to ensure there's no malfunctioning QList APIs
|
||||
std::shared_ptr<int> heapData = std::shared_ptr<int>(new int(42));
|
||||
|
||||
enum State { Constructed = 106, Destructed = 110 };
|
||||
State state;
|
||||
@ -381,6 +385,28 @@ private slots:
|
||||
void reinsertToEndInt_qtbug91360() const { reinsertToEnd<int>(); }
|
||||
void reinsertToEndMovable_qtbug91360() const { reinsertToEnd<Movable>(); }
|
||||
void reinsertToEndCustom_qtbug91360() const { reinsertToEnd<Custom>(); }
|
||||
void reinsertRangeToEndInt_qtbug91360() const { reinsertRangeToEnd<int>(); }
|
||||
void reinsertRangeToEndMovable_qtbug91360() const { reinsertRangeToEnd<Movable>(); }
|
||||
void reinsertRangeToEndCustom_qtbug91360() const { reinsertRangeToEnd<Custom>(); }
|
||||
// QList reference stability tests:
|
||||
void stability_reserveInt() const { stability_reserve<int>(); }
|
||||
void stability_reserveMovable() const { stability_reserve<Movable>(); }
|
||||
void stability_reserveCustom() const { stability_reserve<Custom>(); }
|
||||
void stability_eraseInt() const { stability_erase<int>(); }
|
||||
void stability_eraseMovable() const { stability_erase<Movable>(); }
|
||||
void stability_eraseCustom() const { stability_erase<Custom>(); }
|
||||
void stability_appendInt() const { stability_append<int>(); }
|
||||
void stability_appendMovable() const { stability_append<Movable>(); }
|
||||
void stability_appendCustom() const { stability_append<Custom>(); }
|
||||
void stability_insertElementInt() const { stability_insertElement<int>(); }
|
||||
void stability_insertElementMovable() const { stability_insertElement<Movable>(); }
|
||||
void stability_insertElementCustom() const { stability_insertElement<Custom>(); }
|
||||
void stability_emplaceInt() const { stability_emplace<int>(); }
|
||||
void stability_emplaceMovable() const { stability_emplace<Movable>(); }
|
||||
void stability_emplaceCustom() const { stability_emplace<Custom>(); }
|
||||
void stability_resizeInt() const { stability_resize<int>(); }
|
||||
void stability_resizeMovable() const { stability_resize<Movable>(); }
|
||||
void stability_resizeCustom() const { stability_resize<Custom>(); }
|
||||
|
||||
private:
|
||||
template<typename T> void copyConstructor() const;
|
||||
@ -411,10 +437,55 @@ private:
|
||||
template<typename T> void detachThreadSafety() const;
|
||||
template<typename T> void emplaceImpl() const;
|
||||
template<typename T> void emplaceConsistentWithStdVectorImpl() const;
|
||||
template<typename T, typename Reinsert>
|
||||
void reinsert(Reinsert op) const;
|
||||
template<typename T>
|
||||
void reinsertToBegin() const;
|
||||
void reinsertToBegin() const
|
||||
{
|
||||
reinsert<T>([](QList<T> &list) {
|
||||
list.prepend(list.back());
|
||||
list.removeLast();
|
||||
});
|
||||
}
|
||||
template<typename T>
|
||||
void reinsertToEnd() const;
|
||||
void reinsertToEnd() const
|
||||
{
|
||||
reinsert<T>([](QList<T> &list) {
|
||||
list.append(list.front());
|
||||
list.removeFirst();
|
||||
});
|
||||
}
|
||||
template<typename T>
|
||||
void reinsertRangeToEnd() const
|
||||
{
|
||||
reinsert<T>([](QList<T> &list) {
|
||||
list.append(list.begin(), list.begin() + 1);
|
||||
list.removeFirst();
|
||||
});
|
||||
}
|
||||
template<typename T>
|
||||
void stability_reserve() const;
|
||||
template<typename T>
|
||||
void stability_erase() const;
|
||||
template<typename T>
|
||||
void stability_append() const;
|
||||
template<typename T, typename Insert>
|
||||
void stability_insert(Insert op) const;
|
||||
template<typename T>
|
||||
void stability_resize() const;
|
||||
|
||||
template<typename T>
|
||||
void stability_insertElement() const
|
||||
{
|
||||
stability_insert<T>(
|
||||
[](QList<T> &list, int pos, const T &value) { list.insert(pos, 1, value); });
|
||||
}
|
||||
template<typename T>
|
||||
void stability_emplace() const
|
||||
{
|
||||
stability_insert<T>(
|
||||
[](QList<T> &list, int pos, const T &value) { list.emplace(pos, value); });
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@ -452,6 +523,18 @@ const Custom SimpleValue<Custom>::Values[] = { 110, 105, 101, 114, 111, 98 };
|
||||
#define T_DOG SimpleValue<T>::at(4)
|
||||
#define T_BLAH SimpleValue<T>::at(5)
|
||||
|
||||
// returns a pair of QList<T> and QList<T *>
|
||||
template<typename It>
|
||||
decltype(auto) qlistCopyAndReferenceFromRange(It first, It last)
|
||||
{
|
||||
using T = typename std::iterator_traits<It>::value_type;
|
||||
QList<T> copy(first, last);
|
||||
QList<T *> reference;
|
||||
for (; first != last; ++first)
|
||||
reference.append(std::addressof(*first));
|
||||
return std::make_pair(copy, reference);
|
||||
}
|
||||
|
||||
void tst_QList::constructors_empty() const
|
||||
{
|
||||
QList<int> emptyInt;
|
||||
@ -2345,6 +2428,10 @@ void tst_QList::reserveZero()
|
||||
vec.append(42);
|
||||
QCOMPARE(vec.size(), 1);
|
||||
QVERIFY(vec.capacity() >= 1);
|
||||
|
||||
QList<int> vec2;
|
||||
vec2.reserve(0); // should not crash either
|
||||
vec2.reserve(-1);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
@ -2854,51 +2941,294 @@ void tst_QList::reallocateCustomAlignedType_qtbug90359() const
|
||||
QCOMPARE(actual, expected);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void tst_QList::reinsertToBegin() const
|
||||
template<typename T, typename Reinsert>
|
||||
void tst_QList::reinsert(Reinsert op) const
|
||||
{
|
||||
QList<T> list(1);
|
||||
const auto reinsert = [](QList<T> &list) {
|
||||
list.prepend(list.back());
|
||||
list.removeLast();
|
||||
};
|
||||
TST_QLIST_CHECK_LEAKS(T)
|
||||
|
||||
QList<T> list(1);
|
||||
// this constant is big enough for the QList to stop reallocating, after
|
||||
// all, size is always less than 3
|
||||
const int maxIters = 128;
|
||||
for (int i = 0; i < maxIters; ++i) {
|
||||
reinsert(list);
|
||||
op(list);
|
||||
}
|
||||
|
||||
// if QList continues to grow, it's an error
|
||||
qsizetype capacity = list.capacity();
|
||||
for (int i = 0, enoughIters = int(capacity) * 2; i < enoughIters; ++i) {
|
||||
reinsert(list);
|
||||
op(list);
|
||||
QCOMPARE(capacity, list.capacity());
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void tst_QList::reinsertToEnd() const
|
||||
void tst_QList::stability_reserve() const
|
||||
{
|
||||
QList<T> list(1);
|
||||
const auto reinsert = [](QList<T> &list) {
|
||||
list.append(list.front());
|
||||
list.removeFirst();
|
||||
};
|
||||
TST_QLIST_CHECK_LEAKS(T)
|
||||
|
||||
// this constant is big enough for the QList to stop reallocating, after
|
||||
// all, size is always less than 3
|
||||
const int maxIters = 128;
|
||||
for (int i = 0; i < maxIters; ++i) {
|
||||
reinsert(list);
|
||||
// NOTE: this test verifies that QList::constData() stays unchanged when
|
||||
// inserting as much as requested by the reserve. This is specifically
|
||||
// designed this way as in cases when QTypeInfo<T>::isRelocatable returns
|
||||
// true, reallocation might use fast ::realloc() path which may in theory
|
||||
// (and, actually, in practice) just expand the current memory area and thus
|
||||
// keep QList::constData() unchanged, which means checks like
|
||||
// QVERIFY(oldConstData != vec.constData()) are flaky. When
|
||||
// QTypeInfo<T>::isRelocatable returns false, constData() will always change
|
||||
// if a reallocation happens and this will fail the test. This should be
|
||||
// sufficient on its own to test the stability requirements.
|
||||
|
||||
{
|
||||
QList<T> vec;
|
||||
vec.reserve(64);
|
||||
const T *ptr = vec.constData();
|
||||
vec.append(QList<T>(64));
|
||||
QCOMPARE(ptr, vec.constData());
|
||||
}
|
||||
|
||||
// if QList continues to grow, it's an error
|
||||
qsizetype capacity = list.capacity();
|
||||
for (int i = 0, enoughIters = int(capacity) * 2; i < enoughIters; ++i) {
|
||||
reinsert(list);
|
||||
QCOMPARE(capacity, list.capacity());
|
||||
{
|
||||
QList<T> vec;
|
||||
vec.prepend(SimpleValue<T>::at(0));
|
||||
vec.removeFirst();
|
||||
vec.reserve(64);
|
||||
const T *ptr = vec.constData();
|
||||
vec.append(QList<T>(64));
|
||||
QCOMPARE(ptr, vec.constData());
|
||||
}
|
||||
|
||||
{
|
||||
QList<T> vec;
|
||||
const T *ptr = vec.constData();
|
||||
vec.reserve(vec.capacity());
|
||||
QCOMPARE(ptr, vec.constData());
|
||||
vec.append(QList<T>(vec.capacity()));
|
||||
QCOMPARE(ptr, vec.constData());
|
||||
}
|
||||
|
||||
{
|
||||
QList<T> vec;
|
||||
vec.prepend(SimpleValue<T>::at(0));
|
||||
vec.removeFirst();
|
||||
vec.reserve(vec.capacity());
|
||||
const T *ptr = vec.constData();
|
||||
vec.append(QList<T>(vec.capacity()));
|
||||
QCOMPARE(ptr, vec.constData());
|
||||
}
|
||||
|
||||
{
|
||||
QList<T> vec;
|
||||
vec.append(SimpleValue<T>::at(0));
|
||||
vec.reserve(64);
|
||||
const T *ptr = vec.constData();
|
||||
vec.append(QList<T>(64 - vec.size())); // 1 element is already in the container
|
||||
QCOMPARE(ptr, vec.constData());
|
||||
QCOMPARE(vec.size(), 64);
|
||||
QCOMPARE(vec.capacity(), 64);
|
||||
const qsizetype oldCapacity = vec.capacity();
|
||||
vec.append(SimpleValue<T>::at(1)); // will reallocate as this exceeds 64
|
||||
QVERIFY(oldCapacity < vec.capacity());
|
||||
}
|
||||
|
||||
{
|
||||
QList<T> vec;
|
||||
vec.prepend(SimpleValue<T>::at(0));
|
||||
vec.reserve(64);
|
||||
const T *ptr = vec.constData();
|
||||
vec.append(QList<T>(64 - vec.size())); // 1 element is already in the container
|
||||
QCOMPARE(ptr, vec.constData());
|
||||
QCOMPARE(vec.size(), 64);
|
||||
QCOMPARE(vec.capacity(), 64);
|
||||
const qsizetype oldCapacity = vec.capacity();
|
||||
vec.append(SimpleValue<T>::at(1)); // will reallocate as this exceeds 64
|
||||
QVERIFY(oldCapacity < vec.capacity());
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void tst_QList::stability_erase() const
|
||||
{
|
||||
TST_QLIST_CHECK_LEAKS(T)
|
||||
|
||||
// invalidated: [pos, end())
|
||||
for (int pos = 1; pos < 10; ++pos) {
|
||||
QList<T> v(10);
|
||||
int k = 0;
|
||||
std::generate(v.begin(), v.end(), [&k]() { return SimpleValue<T>::at(k++); });
|
||||
const auto ptr = v.constData();
|
||||
|
||||
auto [copy, reference] = qlistCopyAndReferenceFromRange(v.begin(), v.begin() + pos);
|
||||
|
||||
v.remove(pos, 1);
|
||||
QVERIFY(ptr == v.constData());
|
||||
for (int i = 0; i < copy.size(); ++i)
|
||||
QCOMPARE(*reference[i], copy[i]);
|
||||
}
|
||||
|
||||
// 0 is a special case, because all values get invalidated
|
||||
{
|
||||
QList<T> v(10);
|
||||
const auto ptr = v.constData();
|
||||
v.remove(0, 2);
|
||||
QVERIFY(ptr != v.constData()); // can do fast removal from begin()
|
||||
}
|
||||
|
||||
// when erasing everything, leave the data pointer in place (not strictly
|
||||
// required, but this makes more sense in general)
|
||||
{
|
||||
QList<T> v(10);
|
||||
const auto ptr = v.constData();
|
||||
v.remove(0, v.size());
|
||||
QVERIFY(ptr == v.constData());
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void tst_QList::stability_append() const
|
||||
{
|
||||
TST_QLIST_CHECK_LEAKS(T)
|
||||
|
||||
{
|
||||
QList<T> v(10);
|
||||
int k = 0;
|
||||
std::generate(v.begin(), v.end(), [&k]() { return SimpleValue<T>::at(k++); });
|
||||
QList<T> src(1, SimpleValue<T>::at(0));
|
||||
v.append(src.begin(), src.end());
|
||||
QVERIFY(v.size() < v.capacity());
|
||||
|
||||
for (int i = 0; i < v.capacity() - v.size(); ++i) {
|
||||
auto [copy, reference] = qlistCopyAndReferenceFromRange(v.begin(), v.end());
|
||||
v.append(SimpleValue<T>::at(i));
|
||||
for (int i = 0; i < copy.size(); ++i)
|
||||
QCOMPARE(*reference[i], copy[i]);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
QList<T> v;
|
||||
v.reserve(10);
|
||||
const qsizetype capacity = v.capacity();
|
||||
const T *ptr = v.constData();
|
||||
v.prepend(SimpleValue<T>::at(0));
|
||||
// here we abuse the internal details of QList. since there's enough
|
||||
// free space, QList should've only rearranged the data in memory,
|
||||
// without reallocating.
|
||||
QCOMPARE(capacity, v.capacity()); // otherwise cannot rely on ptr
|
||||
const qsizetype freeSpaceAtBegin = v.constData() - ptr;
|
||||
const qsizetype freeSpaceAtEnd = v.capacity() - v.size() - freeSpaceAtBegin;
|
||||
QVERIFY(freeSpaceAtEnd > 0); // otherwise this test is useless
|
||||
QVERIFY(v.size() + freeSpaceAtBegin + freeSpaceAtEnd == v.capacity());
|
||||
|
||||
for (int i = 0; i < freeSpaceAtEnd; ++i) {
|
||||
auto [copy, reference] = qlistCopyAndReferenceFromRange(v.begin(), v.end());
|
||||
QList<T> src(1, SimpleValue<T>::at(i));
|
||||
v.append(src.begin(), src.end());
|
||||
for (int i = 0; i < copy.size(); ++i)
|
||||
QCOMPARE(*reference[i], copy[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T, typename Insert>
|
||||
void tst_QList::stability_insert(Insert op) const
|
||||
{
|
||||
TST_QLIST_CHECK_LEAKS(T)
|
||||
|
||||
// invalidated: [pos, end())
|
||||
for (int pos = 1; pos <= 10; ++pos) {
|
||||
QList<T> v(10);
|
||||
int k = 0;
|
||||
std::generate(v.begin(), v.end(), [&k]() { return SimpleValue<T>::at(k++); });
|
||||
v.append(SimpleValue<T>::at(0)); // causes growth
|
||||
v.removeLast();
|
||||
QCOMPARE(v.size(), 10);
|
||||
QVERIFY(v.size() < v.capacity());
|
||||
|
||||
auto [copy, reference] = qlistCopyAndReferenceFromRange(v.begin(), v.begin() + pos);
|
||||
op(v, pos, SimpleValue<T>::at(0));
|
||||
for (int i = 0; i < pos; ++i)
|
||||
QCOMPARE(*reference[i], copy[i]);
|
||||
}
|
||||
|
||||
for (int pos = 1; pos <= 10; ++pos) {
|
||||
QList<T> v(10);
|
||||
int k = 0;
|
||||
std::generate(v.begin(), v.end(), [&k]() { return SimpleValue<T>::at(k++); });
|
||||
v.prepend(SimpleValue<T>::at(0)); // causes growth and free space at begin > 0
|
||||
v.removeFirst();
|
||||
QCOMPARE(v.size(), 10);
|
||||
QVERIFY(v.size() < v.capacity());
|
||||
|
||||
auto [copy, reference] = qlistCopyAndReferenceFromRange(v.begin(), v.begin() + pos);
|
||||
op(v, pos, SimpleValue<T>::at(0));
|
||||
for (int i = 0; i < pos; ++i)
|
||||
QCOMPARE(*reference[i], copy[i]);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void tst_QList::stability_resize() const
|
||||
{
|
||||
TST_QLIST_CHECK_LEAKS(T)
|
||||
|
||||
{
|
||||
QList<T> v(10);
|
||||
v.reserve(15);
|
||||
QVERIFY(v.size() < v.capacity());
|
||||
int k = 0;
|
||||
std::generate(v.begin(), v.end(), [&k]() { return SimpleValue<T>::at(k++); });
|
||||
auto [copy, reference] = qlistCopyAndReferenceFromRange(v.begin(), v.end());
|
||||
|
||||
v.resize(15);
|
||||
for (int i = 0; i < copy.size(); ++i)
|
||||
QCOMPARE(*reference[i], copy[i]);
|
||||
}
|
||||
|
||||
{
|
||||
QList<T> v(10);
|
||||
int k = 0;
|
||||
std::generate(v.begin(), v.end(), [&k]() { return SimpleValue<T>::at(k++); });
|
||||
auto [copy, reference] = qlistCopyAndReferenceFromRange(v.begin(), v.end());
|
||||
|
||||
v.resize(10);
|
||||
for (int i = 0; i < 10; ++i)
|
||||
QCOMPARE(*reference[i], copy[i]);
|
||||
}
|
||||
|
||||
{
|
||||
QList<T> v(10);
|
||||
int k = 0;
|
||||
std::generate(v.begin(), v.end(), [&k]() { return SimpleValue<T>::at(k++); });
|
||||
auto [copy, reference] = qlistCopyAndReferenceFromRange(v.begin(), v.end());
|
||||
|
||||
v.resize(5);
|
||||
for (int i = 0; i < 5; ++i)
|
||||
QCOMPARE(*reference[i], copy[i]);
|
||||
}
|
||||
|
||||
// special case due to prepend:
|
||||
{
|
||||
QList<T> v;
|
||||
v.reserve(20);
|
||||
const qsizetype capacity = v.capacity();
|
||||
const T *ptr = v.constData();
|
||||
v.prepend(SimpleValue<T>::at(0)); // now there's free space at begin
|
||||
v.resize(10);
|
||||
QVERIFY(v.size() < v.capacity());
|
||||
// here we abuse the internal details of QList. since there's enough
|
||||
// free space, QList should've only rearranged the data in memory,
|
||||
// without reallocating.
|
||||
QCOMPARE(capacity, v.capacity()); // otherwise cannot rely on ptr
|
||||
const qsizetype freeSpaceAtBegin = v.constData() - ptr;
|
||||
const qsizetype freeSpaceAtEnd = v.capacity() - v.size() - freeSpaceAtBegin;
|
||||
QVERIFY(freeSpaceAtEnd > 0); // otherwise this test is useless
|
||||
QVERIFY(v.size() + freeSpaceAtBegin + freeSpaceAtEnd == v.capacity());
|
||||
int k = 0;
|
||||
std::generate(v.begin(), v.end(), [&k]() { return SimpleValue<T>::at(k++); });
|
||||
auto [copy, reference] = qlistCopyAndReferenceFromRange(v.begin(), v.end());
|
||||
|
||||
v.resize(v.size() + freeSpaceAtEnd);
|
||||
for (int i = 0; i < copy.size(); ++i)
|
||||
QCOMPARE(*reference[i], copy[i]);
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user