Revision control
Copy as Markdown
// Copyright (C) 2021 The Qt Company Ltd.
// Copyright (C) 2022 Intel Corporation.
// Copyright (C) 2021 Klarälvdalens Datakonsult AB.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#define QT_SHAREDPOINTER_TRACK_POINTERS
#include "qsharedpointer.h"
#include <QTest>
#include <QtTest/private/qcomparisontesthelper_p.h>
#include <QPointer>
#include <QRandomGenerator>
#include <QtCore/QHash>
#include <QtCore/QList>
#include <QtCore/QMap>
#include <QtCore/QThread>
#include <QtCore/private/qvolatile_p.h>
#include "forwarddeclared.h"
#include "nontracked.h"
#include "wrapper.h"
#include <array>
#include <memory>
#include <stdlib.h>
#include <time.h>
#if defined(Q_OS_UNIX) && !defined(Q_OS_INTEGRITY)
#include <sys/resource.h>
#endif
QT_BEGIN_NAMESPACE
namespace QtSharedPointer {
Q_CORE_EXPORT void internalSafetyCheckCleanCheck();
}
QT_END_NAMESPACE
class tst_QSharedPointer: public QObject
{
Q_OBJECT
private slots:
void initTestCase();
void compareCompiles();
void basics_data();
void basics();
void operators();
void nullptrOps();
void swap();
void moveSemantics();
void useOfForwardDeclared();
void memoryManagement();
void dropLastReferenceOfForwardDeclared();
void nonVirtualDestructors();
void lock();
void downCast();
void functionCallDownCast();
void upCast();
void qobjectWeakManagement();
void weakQObjectFromSharedPointer();
void objectCast();
void objectCastStdSharedPtr();
void objectCastFailureNoLeak();
void differentPointers();
void virtualBaseDifferentPointers();
void virtualBaseWeakPointerConversions();
#ifndef QTEST_NO_RTTI
void dynamicCast();
void dynamicCastDifferentPointers();
void dynamicCastVirtualBase();
void dynamicCastFailure();
void dynamicCastFailureNoLeak();
#endif
void constCorrectness();
void customDeleter();
void lambdaCustomDeleter();
void customDeleterOnNullptr();
void creating();
void creatingCvQualified();
void creatingVariadic();
void creatingQObject();
void mixTrackingPointerCode();
void reentrancyWhileDestructing();
void map();
void hash();
void qvariantCast();
void sharedFromThis();
void constructorThrow();
void overloads();
void threadStressTest_data();
void threadStressTest();
void validConstructs();
#if 0
void invalidConstructs_data();
void invalidConstructs();
#endif
void ownerComparisons();
// let invalidConstructs be the last test, because it's the slowest;
// add new tests above this block
public slots:
void cleanup() { safetyCheck(); }
public:
inline void safetyCheck()
{
#ifdef QT_BUILD_INTERNAL
QtSharedPointer::internalSafetyCheckCleanCheck();
#endif
}
};
void tst_QSharedPointer::initTestCase()
{
#if defined(Q_OS_UNIX) && !defined(Q_OS_INTEGRITY)
// The tests create a lot of threads, which require file descriptors. On systems like
// OS X low defaults such as 256 as the limit for the number of simultaneously
// open files is not sufficient.
struct rlimit numFiles;
if (getrlimit(RLIMIT_NOFILE, &numFiles) == 0 && numFiles.rlim_cur < 1024) {
numFiles.rlim_cur = qMin(rlim_t(1024), numFiles.rlim_max);
setrlimit(RLIMIT_NOFILE, &numFiles);
}
#endif
}
template<typename T> static inline
QtSharedPointer::ExternalRefCountData *refCountData(const QSharedPointer<T> &b)
{
// access d-pointer:
struct Dummy {
void* value;
QtSharedPointer::ExternalRefCountData* data;
};
// sanity checks:
static_assert(sizeof(QSharedPointer<T>) == sizeof(Dummy));
Q_ASSERT(static_cast<const Dummy*>(static_cast<const void*>(&b))->value == b.data());
return static_cast<const Dummy*>(static_cast<const void*>(&b))->data;
}
class Data
{
public:
static int destructorCounter;
static int generationCounter;
int generation;
Data() : generation(++generationCounter)
{ }
virtual ~Data()
{
if (generation <= 0)
qFatal("tst_qsharedpointer: Double deletion!");
generation = 0;
++destructorCounter;
}
void doDelete()
{
delete this;
}
bool alsoDelete()
{
doDelete();
return true;
}
virtual void virtualDelete()
{
delete this;
}
virtual int classLevel() { return 1; }
};
int Data::generationCounter = 0;
int Data::destructorCounter = 0;
struct NoDefaultConstructor1
{
int i;
NoDefaultConstructor1(int i) : i(i) {}
NoDefaultConstructor1(uint j) : i(j + 42) {}
};
struct NoDefaultConstructorRef1
{
int &i;
NoDefaultConstructorRef1(int &i) : i(i) {}
};
struct NoDefaultConstructor2
{
void *ptr;
int i;
NoDefaultConstructor2(void *ptr, int i) : ptr(ptr), i(i) {}
};
struct NoDefaultConstructorRef2
{
QString str;
int i;
NoDefaultConstructorRef2(QString &str, int i) : str(str), i(i) {}
};
struct NoDefaultConstructorConstRef2
{
QString str;
int i;
NoDefaultConstructorConstRef2(const QString &str, int i) : str(str), i(i) {}
NoDefaultConstructorConstRef2(const QByteArray &ba, int i = 42) : str(QString::fromLatin1(ba)), i(i) {}
};
struct NoDefaultConstructorRRef1
{
int &i;
NoDefaultConstructorRRef1(int &&i) : i(i) {}
};
struct NoDefaultConstructorRRef2
{
std::unique_ptr<int> i;
NoDefaultConstructorRRef2(std::unique_ptr<int> &&i) : i(std::move(i)) {}
};
class DerivedData;
void tst_QSharedPointer::compareCompiles()
{
QTestPrivate::testAllComparisonOperatorsCompile<QSharedPointer<Data>>();
QTestPrivate::testAllComparisonOperatorsCompile<QSharedPointer<Data>, Data*>();
QTestPrivate::testAllComparisonOperatorsCompile<QSharedPointer<Data>, std::nullptr_t>();
QTestPrivate::testAllComparisonOperatorsCompile<QSharedPointer<QObject>,
QSharedPointer<QWidget>>();
QTestPrivate::testAllComparisonOperatorsCompile<QSharedPointer<Data>, DerivedData*>();
QTestPrivate::testAllComparisonOperatorsCompile<QSharedPointer<DerivedData>, Data*>();
}
void tst_QSharedPointer::basics_data()
{
QTest::addColumn<bool>("isNull");
QTest::newRow("null") << true;
QTest::newRow("non-null") << false;
}
void tst_QSharedPointer::basics()
{
{
QSharedPointer<Data> ptr;
QWeakPointer<Data> weakref;
QCOMPARE(sizeof(ptr), 2*sizeof(void*));
QCOMPARE(sizeof(weakref), 2*sizeof(void*));
}
{
QSharedPointer<const Data> ptr;
QWeakPointer<const Data> weakref;
QCOMPARE(sizeof(ptr), 2*sizeof(void*));
QCOMPARE(sizeof(weakref), 2*sizeof(void*));
}
QFETCH(bool, isNull);
Data *aData = 0;
if (!isNull)
aData = new Data;
Data *otherData = new Data;
QSharedPointer<Data> ptr(aData);
{
// basic self tests
QCOMPARE(ptr.isNull(), isNull);
QCOMPARE(bool(ptr), !isNull);
QCOMPARE(!ptr, isNull);
QCOMPARE(ptr.data(), aData);
QCOMPARE(ptr.get(), aData);
QCOMPARE(ptr.operator->(), aData);
if (!isNull) {
Data &dataReference = *ptr;
QCOMPARE(&dataReference, aData);
}
QT_TEST_ALL_COMPARISON_OPS(ptr, aData, Qt::strong_ordering::equal);
QT_TEST_EQUALITY_OPS(ptr, otherData, false);
}
QVERIFY(!refCountData(ptr) || refCountData(ptr)->weakref.loadRelaxed() == 1);
QVERIFY(!refCountData(ptr) || refCountData(ptr)->strongref.loadRelaxed() == 1);
{
// create another object:
QSharedPointer<Data> otherCopy(otherData);
QT_TEST_EQUALITY_OPS(ptr, otherCopy, false);
// otherData is deleted here
}
QVERIFY(!refCountData(ptr) || refCountData(ptr)->weakref.loadRelaxed() == 1);
QVERIFY(!refCountData(ptr) || refCountData(ptr)->strongref.loadRelaxed() == 1);
{
// create a copy:
QSharedPointer<Data> copy(ptr);
QT_TEST_ALL_COMPARISON_OPS(ptr, copy, Qt::strong_ordering::equal);
QCOMPARE(copy, ptr);
QCOMPARE(ptr, copy);
QCOMPARE(copy.isNull(), isNull);
QCOMPARE(copy.data(), aData);
QCOMPARE(copy.get(), aData);
QVERIFY(copy == aData);
}
QVERIFY(!refCountData(ptr) || refCountData(ptr)->weakref.loadRelaxed() == 1);
QVERIFY(!refCountData(ptr) || refCountData(ptr)->strongref.loadRelaxed() == 1);
{
// create a weak reference:
QWeakPointer<Data> weak(ptr);
QCOMPARE(weak.isNull(), isNull);
QCOMPARE(!weak, isNull);
QCOMPARE(bool(weak), !isNull);
QCOMPARE(weak.isNull(), (weak == nullptr));
QCOMPARE(weak.isNull(), (nullptr == weak));
QCOMPARE(!weak.isNull(), (weak != nullptr));
QCOMPARE(!weak.isNull(), (nullptr != weak));
QVERIFY(ptr == weak);
QVERIFY(weak == ptr);
QVERIFY(! (ptr != weak));
QVERIFY(! (weak != ptr));
// create another reference:
QWeakPointer<Data> weak2(weak);
QCOMPARE(weak2.isNull(), isNull);
QCOMPARE(!weak2, isNull);
QCOMPARE(bool(weak2), !isNull);
QVERIFY(weak2 == weak);
QVERIFY(weak == weak2);
QVERIFY(! (weak2 != weak));
QVERIFY(! (weak != weak2));
// create a strong reference back:
QSharedPointer<Data> strong(weak);
QVERIFY(strong == weak);
QVERIFY(strong == ptr);
QCOMPARE(strong.data(), aData);
QCOMPARE(strong.get(), aData);
}
QVERIFY(!refCountData(ptr) || refCountData(ptr)->weakref.loadRelaxed() == 1);
QVERIFY(!refCountData(ptr) || refCountData(ptr)->strongref.loadRelaxed() == 1);
// aData is deleted here
}
void tst_QSharedPointer::operators()
{
QSharedPointer<char> p1;
QSharedPointer<char> p2(new char);
qptrdiff diff = p2.data() - p1.data();
QVERIFY(p1.data() != p2.data());
QVERIFY(p1.get() != p2.get());
QVERIFY(diff != 0);
// operator-
QCOMPARE(p2 - p1.data(), diff);
QCOMPARE(p2 - p1.get(), diff);
QCOMPARE(p2.data() - p1, diff);
QCOMPARE(p2.get() - p1, diff);
QCOMPARE(p2 - p1, diff);
QCOMPARE(p1 - p2, -diff);
QCOMPARE(p1 - p1, qptrdiff(0));
QCOMPARE(p2 - p2, qptrdiff(0));
// operator<
QVERIFY(p1 < p2.data());
QVERIFY(p1 < p2.get());
QVERIFY(p1.data() < p2);
QVERIFY(p1.get() < p2);
QT_TEST_ALL_COMPARISON_OPS(p1, p2, Qt::strong_ordering::less);
QT_TEST_ALL_COMPARISON_OPS(p2, p2, Qt::strong_ordering::equal);
QT_TEST_ALL_COMPARISON_OPS(p1, p1, Qt::strong_ordering::equal);
// qHash
QCOMPARE(qHash(p1), qHash(p1.data()));
QCOMPARE(qHash(p1), qHash(p1.get()));
QCOMPARE(qHash(p2), qHash(p2.data()));
QCOMPARE(qHash(p2), qHash(p2.get()));
}
void tst_QSharedPointer::nullptrOps()
{
QSharedPointer<char> p1(nullptr);
QSharedPointer<char> p2 = nullptr;
QSharedPointer<char> null;
QT_TEST_ALL_COMPARISON_OPS(p1, null, Qt::strong_ordering::equal);
QT_TEST_ALL_COMPARISON_OPS(p1, nullptr, Qt::strong_ordering::equal);
QVERIFY(!p1);
QVERIFY(!p1.data());
QVERIFY(!p1.get());
QT_TEST_ALL_COMPARISON_OPS(p2, null, Qt::strong_ordering::equal);
QT_TEST_ALL_COMPARISON_OPS(p2, nullptr, Qt::strong_ordering::equal);
QVERIFY(!p2);
QVERIFY(!p2.data());
QVERIFY(!p2.get());
QT_TEST_ALL_COMPARISON_OPS(p1, p2, Qt::strong_ordering::equal);
QWeakPointer<char> wp1 = p1;
QVERIFY(wp1 == nullptr);
QVERIFY(nullptr == wp1);
QCOMPARE(wp1, nullptr);
QCOMPARE(nullptr, wp1);
QSharedPointer<char> p3 = p1;
QT_TEST_ALL_COMPARISON_OPS(p1, p3, Qt::strong_ordering::equal);
QT_TEST_ALL_COMPARISON_OPS(p3, null, Qt::strong_ordering::equal);
QT_TEST_ALL_COMPARISON_OPS(p3, nullptr, Qt::strong_ordering::equal);
QVERIFY(!p3.data());
QVERIFY(!p3.get());
p3 = nullptr;
// check for non-ambiguity
QSharedPointer<char> p1_zero(0);
QSharedPointer<char> p2_zero = 0;
p3 = 0;
QSharedPointer<char> p4(new char);
QVERIFY(p4);
QVERIFY(p4.data());
QVERIFY(p4.get());
QT_TEST_EQUALITY_OPS(p4, nullptr, false);
QT_TEST_EQUALITY_OPS(p4, p1, false);
QT_TEST_EQUALITY_OPS(p4, p2, false);
QT_TEST_EQUALITY_OPS(p4, null, false);
QT_TEST_EQUALITY_OPS(p4, p3, false);
QWeakPointer<char> wp2 = p4;
QVERIFY(wp2 != nullptr);
QVERIFY(nullptr != wp2);
}
void tst_QSharedPointer::swap()
{
QSharedPointer<int> p1, p2(new int(42)), control = p2;
QVERIFY(p1 != control);
QVERIFY(p1.isNull());
QVERIFY(p2 == control);
QVERIFY(!p2.isNull());
QVERIFY(*p2 == 42);
p1.swap(p2);
QVERIFY(p1 == control);
QVERIFY(!p1.isNull());
QVERIFY(p2 != control);
QVERIFY(p2.isNull());
QVERIFY(*p1 == 42);
p1.swap(p2);
QVERIFY(p1 != control);
QVERIFY(p1.isNull());
QVERIFY(p2 == control);
QVERIFY(!p2.isNull());
QVERIFY(*p2 == 42);
qSwap(p1, p2);
QVERIFY(p1 == control);
QVERIFY(!p1.isNull());
QVERIFY(p2 != control);
QVERIFY(p2.isNull());
QVERIFY(*p1 == 42);
QWeakPointer<int> w1, w2 = control;
QVERIFY(w1.isNull());
QVERIFY(!w2.isNull());
QVERIFY(w2.lock() == control);
QVERIFY(!w1.lock());
w1.swap(w2);
QVERIFY(w2.isNull());
QVERIFY(!w1.isNull());
QVERIFY(w1.lock() == control);
QVERIFY(!w2.lock());
qSwap(w1, w2);
QVERIFY(w1.isNull());
QVERIFY(w2.lock() == control);
p1.reset();
p2.reset();
control.reset();
QVERIFY(w1.isNull());
QVERIFY(w2.isNull());
}
void tst_QSharedPointer::moveSemantics()
{
QSharedPointer<int> p1, p2(new int(42)), control = p2;
QVERIFY(p1 != control);
QVERIFY(p1.isNull());
QVERIFY(p2 == control);
QVERIFY(!p2.isNull());
QVERIFY(*p2 == 42);
// move assignment
p1 = std::move(p2);
QVERIFY(p1 == control);
QVERIFY(!p1.isNull());
QVERIFY(p2 != control);
QVERIFY(p2.isNull());
QVERIFY(*p1 == 42);
// move construction
QSharedPointer<int> p3 = std::move(p1);
QVERIFY(p1 != control);
QVERIFY(p1.isNull());
QVERIFY(p3 == control);
QVERIFY(!p3.isNull());
QVERIFY(*p3 == 42);
QWeakPointer<int> w1, w2 = control;
QVERIFY(w1.isNull());
QVERIFY(!w2.isNull());
QVERIFY(w2.lock() == control);
QVERIFY(!w1.lock());
// move assignment
w1 = std::move(w2);
QVERIFY(w2.isNull());
QVERIFY(!w1.isNull());
QVERIFY(w1.lock() == control);
QVERIFY(!w2.lock());
// move construction
QWeakPointer<int> w3 = std::move(w1);
QVERIFY(w1.isNull());
QVERIFY(!w3.isNull());
QVERIFY(w3.lock() == control);
QVERIFY(!w1.lock());
p1.reset();
p2.reset();
p3.reset();
control.reset();
QVERIFY(w1.isNull());
QVERIFY(w2.isNull());
QVERIFY(w3.isNull());
}
void tst_QSharedPointer::useOfForwardDeclared()
{
// this just a compile test: use the forward-declared class
QSharedPointer<ForwardDeclared> sp;
// copying should work, too:
QSharedPointer<ForwardDeclared> sp2 = sp;
// and assignment:
QSharedPointer<ForwardDeclared> sp3;
sp3 = sp;
// move assignment:
QSharedPointer<ForwardDeclared> sp4;
sp4 = std::move(sp);
// and move constuction:
QSharedPointer<ForwardDeclared> sp5 = std::move(sp2);
// swapping:
sp4.swap(sp3);
qSwap(sp4, sp3);
// and, of course, destruction
}
void tst_QSharedPointer::memoryManagement()
{
QT_WARNING_PUSH
QT_WARNING_DISABLE_CLANG("-Wself-assign-overloaded")
int generation = Data::generationCounter + 1;
int destructorCounter = Data::destructorCounter;
QSharedPointer<Data> ptr = QSharedPointer<Data>(new Data);
QCOMPARE(ptr->generation, generation);
QCOMPARE(Data::destructorCounter, destructorCounter);
QCOMPARE(Data::generationCounter, generation);
ptr = ptr;
QCOMPARE(ptr->generation, generation);
QCOMPARE(Data::destructorCounter, destructorCounter);
QCOMPARE(Data::generationCounter, generation);
{
QSharedPointer<Data> copy = ptr;
QCOMPARE(ptr->generation, generation);
QCOMPARE(copy->generation, generation);
// copy goes out of scope, ptr continues
}
QCOMPARE(ptr->generation, generation);
QCOMPARE(Data::destructorCounter, destructorCounter);
QCOMPARE(Data::generationCounter, generation);
{
QWeakPointer<Data> weak = ptr;
weak = ptr;
QCOMPARE(ptr->generation, generation);
QCOMPARE(Data::destructorCounter, destructorCounter);
QCOMPARE(Data::generationCounter, generation);
weak = weak;
QCOMPARE(ptr->generation, generation);
QCOMPARE(Data::destructorCounter, destructorCounter);
QCOMPARE(Data::generationCounter, generation);
QSharedPointer<Data> strong = weak;
QCOMPARE(ptr->generation, generation);
QCOMPARE(strong->generation, generation);
QCOMPARE(Data::destructorCounter, destructorCounter);
QCOMPARE(Data::generationCounter, generation);
// both weak and strong go out of scope
}
QCOMPARE(ptr->generation, generation);
QCOMPARE(Data::destructorCounter, destructorCounter);
QCOMPARE(Data::generationCounter, generation);
QWeakPointer<Data> weak = ptr;
ptr = QSharedPointer<Data>();
// destructor must have been called
QCOMPARE(Data::destructorCounter, destructorCounter + 1);
QVERIFY(ptr.isNull());
QVERIFY(weak.isNull());
// if we create a strong pointer from the weak, it must still be null
ptr = weak;
QVERIFY(ptr.isNull());
QVERIFY(ptr == 0);
QCOMPARE(ptr.data(), (Data*)0);
QT_WARNING_POP
}
void tst_QSharedPointer::dropLastReferenceOfForwardDeclared()
{
// pointer to shared-pointer is weird, but we need to do it so that
// we can drop the last reference in a different .cpp than where it was created
forwardDeclaredDestructorRunCount = 0;
delete forwardPointer();
QCOMPARE(forwardDeclaredDestructorRunCount, 1);
}
// NVD for "non-virtual destructor"
struct NVDData
{
static int destructorCounter;
~NVDData() { ++destructorCounter; }
int dummy;
};
int NVDData::destructorCounter;
struct NVDDerivedData : NVDData
{
static int destructorCounter;
~NVDDerivedData() { ++destructorCounter; }
};
int NVDDerivedData::destructorCounter;
void tst_QSharedPointer::nonVirtualDestructors()
{
NVDData::destructorCounter = NVDDerivedData::destructorCounter = 0;
{
QSharedPointer<NVDData> ptr(new NVDData);
}
QCOMPARE(NVDData::destructorCounter, 1);
QCOMPARE(NVDDerivedData::destructorCounter, 0);
NVDData::destructorCounter = NVDDerivedData::destructorCounter = 0;
{
QSharedPointer<NVDDerivedData> ptr(new NVDDerivedData);
}
QCOMPARE(NVDData::destructorCounter, 1);
QCOMPARE(NVDDerivedData::destructorCounter, 1);
NVDData::destructorCounter = NVDDerivedData::destructorCounter = 0;
{
QSharedPointer<NVDData> bptr;
QSharedPointer<NVDDerivedData> ptr(new NVDDerivedData);
bptr = ptr;
}
QCOMPARE(NVDData::destructorCounter, 1);
QCOMPARE(NVDDerivedData::destructorCounter, 1);
NVDData::destructorCounter = NVDDerivedData::destructorCounter = 0;
{
QSharedPointer<NVDData> ptr(new NVDDerivedData);
}
QCOMPARE(NVDData::destructorCounter, 1);
QCOMPARE(NVDDerivedData::destructorCounter, 1);
}
void tst_QSharedPointer::lock()
{
QSharedPointer<int> sp = QSharedPointer<int>::create();
QVERIFY(sp);
QWeakPointer<int> wp = sp;
QVERIFY(sp == wp);
QVERIFY(sp == wp.lock());
QVERIFY(sp == wp.toStrongRef());
sp.reset();
QVERIFY(!wp);
QVERIFY(sp != wp); // this is why op(shared_ptr, weak_ptr) is a bad idea (apart from MT races)...
QVERIFY(sp == wp.lock());
QVERIFY(sp == wp.toStrongRef());
}
class DerivedData: public Data
{
public:
static int derivedDestructorCounter;
int moreData;
DerivedData() : moreData(0) { }
~DerivedData() { ++derivedDestructorCounter; }
void virtualDelete() override
{
delete this;
}
int classLevel() override { return 2; }
};
int DerivedData::derivedDestructorCounter = 0;
class Stuffing
{
public:
char buffer[16];
Stuffing() { for (uint i = 0; i < sizeof buffer; ++i) buffer[i] = 16 - i; }
virtual ~Stuffing() { }
};
class DiffPtrDerivedData: public Stuffing, public Data
{
public:
int classLevel() override { return 3; }
};
class VirtualDerived: virtual public Data
{
public:
int moreData;
VirtualDerived() : moreData(0xc0ffee) { }
int classLevel() override { return 4; }
};
void tst_QSharedPointer::downCast()
{
{
// copy construction
QSharedPointer<DerivedData> ptr = QSharedPointer<DerivedData>(new DerivedData);
QSharedPointer<DerivedData> copy = ptr;
QSharedPointer<Data> baseptr = qSharedPointerStaticCast<Data>(ptr);
QSharedPointer<Data> other;
QWeakPointer<DerivedData> weak = ptr;
QWeakPointer<Data> baseweak = qSharedPointerStaticCast<Data>(ptr);
QWeakPointer<Data> baseweak2 = qSharedPointerStaticCast<Data>(weak);
QVERIFY(ptr == baseptr);
QVERIFY(baseptr == ptr);
QVERIFY(! (ptr != baseptr));
QVERIFY(! (baseptr != ptr));
QVERIFY(ptr != other);
QVERIFY(other != ptr);
QVERIFY(! (ptr == other));
QVERIFY(! (other == ptr));
// copy assignments
baseptr = qSharedPointerStaticCast<Data>(ptr);
baseweak = qSharedPointerStaticCast<Data>(ptr);
baseweak2 = baseweak;
// move assignments (these don't actually move)
baseptr = qSharedPointerStaticCast<Data>(std::move(ptr));
ptr = copy;
baseweak = qSharedPointerStaticCast<Data>(std::move(ptr));
ptr = copy;
baseweak2 = qSharedPointerStaticCast<Data>(std::move(baseweak));
// move construction (these don't actually move)
ptr = copy;
QSharedPointer<Data> ptr3(qSharedPointerStaticCast<Data>(std::move(ptr)));
ptr = copy;
QWeakPointer<Data> baseweak3(qSharedPointerStaticCast<Data>(std::move(ptr)));
ptr = copy;
QWeakPointer<Data> baseweak4(qSharedPointerStaticCast<Data>(std::move(weak)));
}
{
// copy construction
QSharedPointer<DerivedData> ptr = QSharedPointer<DerivedData>(new DerivedData);
QSharedPointer<DerivedData> copy = ptr;
QSharedPointer<Data> baseptr = ptr;
QWeakPointer<DerivedData> weak = ptr;
QWeakPointer<Data> baseweak = ptr;
QWeakPointer<Data> baseweak2 = weak;
// copy assignments
baseptr = ptr;
baseweak = ptr;
baseweak2 = weak;
// move assignments (only the QSharedPointer-QSharedPointer actually moves)
baseweak = std::move(ptr);
baseweak2 = std::move(weak);
ptr = copy;
baseptr = std::move(ptr);
// move construction (only the QSharedPointer-QSharedPointer actually moves)
ptr = copy;
QWeakPointer<Data> baseweak3(std::move(ptr));
ptr = copy;
QWeakPointer<Data> baseweak4(std::move(weak));
ptr = copy;
QSharedPointer<Data> baseptr2(std::move(ptr));
}
int destructorCount;
destructorCount = DerivedData::derivedDestructorCounter;
{
QSharedPointer<Data> baseptr;
{
QSharedPointer<DerivedData> ptr = QSharedPointer<DerivedData>(new DerivedData);
baseptr = ptr;
QT_TEST_ALL_COMPARISON_OPS(baseptr, ptr, Qt::strong_ordering::equal);
}
}
QCOMPARE(DerivedData::derivedDestructorCounter, destructorCount + 1);
destructorCount = DerivedData::derivedDestructorCounter;
{
QSharedPointer<DerivedData> ptr = QSharedPointer<DerivedData>(new DerivedData);
QWeakPointer<Data> baseptr = ptr;
QVERIFY(baseptr == ptr);
ptr = QSharedPointer<DerivedData>();
QVERIFY(baseptr.isNull());
}
QCOMPARE(DerivedData::derivedDestructorCounter, destructorCount + 1);
destructorCount = DerivedData::derivedDestructorCounter;
{
QSharedPointer<DerivedData> ptr = QSharedPointer<DerivedData>(new DerivedData);
QWeakPointer<DerivedData> weakptr(ptr);
QSharedPointer<Data> baseptr = weakptr;
QVERIFY(baseptr == ptr);
QWeakPointer<Data> baseweakptr = weakptr;
QVERIFY(baseweakptr == ptr);
}
QCOMPARE(DerivedData::derivedDestructorCounter, destructorCount + 1);
}
void functionDataByValue(QSharedPointer<Data> p) { Q_UNUSED(p); };
void functionDataByRef(const QSharedPointer<Data> &p) { Q_UNUSED(p); };
void tst_QSharedPointer::functionCallDownCast()
{
QSharedPointer<DerivedData> p(new DerivedData());
functionDataByValue(p);
functionDataByRef(p);
}
void tst_QSharedPointer::upCast()
{
QSharedPointer<Data> baseptr = QSharedPointer<Data>(new DerivedData);
{
QSharedPointer<DerivedData> derivedptr = qSharedPointerStaticCast<DerivedData>(baseptr);
QVERIFY(baseptr == derivedptr);
QCOMPARE(static_cast<Data *>(derivedptr.data()), baseptr.data());
}
QCOMPARE(int(refCountData(baseptr)->weakref.loadRelaxed()), 1);
QCOMPARE(int(refCountData(baseptr)->strongref.loadRelaxed()), 1);
{
QWeakPointer<DerivedData> derivedptr = qWeakPointerCast<DerivedData>(baseptr);
QVERIFY(baseptr == derivedptr);
}
QCOMPARE(int(refCountData(baseptr)->weakref.loadRelaxed()), 1);
QCOMPARE(int(refCountData(baseptr)->strongref.loadRelaxed()), 1);
{
QWeakPointer<Data> weakptr = baseptr;
QSharedPointer<DerivedData> derivedptr = qSharedPointerStaticCast<DerivedData>(weakptr);
QVERIFY(baseptr == derivedptr);
QCOMPARE(static_cast<Data *>(derivedptr.data()), baseptr.data());
}
QCOMPARE(int(refCountData(baseptr)->weakref.loadRelaxed()), 1);
QCOMPARE(int(refCountData(baseptr)->strongref.loadRelaxed()), 1);
{
QSharedPointer<DerivedData> derivedptr = baseptr.staticCast<DerivedData>();
QVERIFY(baseptr == derivedptr);
QCOMPARE(static_cast<Data *>(derivedptr.data()), baseptr.data());
}
QCOMPARE(int(refCountData(baseptr)->weakref.loadRelaxed()), 1);
QCOMPARE(int(refCountData(baseptr)->strongref.loadRelaxed()), 1);
}
class OtherObject: public QObject
{
Q_OBJECT
};
void tst_QSharedPointer::qobjectWeakManagement()
{
{
QWeakPointer<QObject> weak;
weak = QWeakPointer<QObject>();
QVERIFY(weak.isNull());
QVERIFY(weak.toStrongRef().isNull());
}
{
QObject *obj = new QObject;
QSharedPointer<QObject> shared(obj);
QWeakPointer<QObject> weak(shared);
QVERIFY(!weak.isNull());
QVERIFY(weak.toStrongRef() == obj);
// now delete
shared.reset();
QVERIFY(weak.isNull());
}
safetyCheck();
{
// same, bit with operator=
QObject *obj = new QObject;
QSharedPointer<QObject> shared(obj);
QWeakPointer<QObject> weak;
weak = shared;
QVERIFY(!weak.isNull());
QVERIFY(weak.toStrongRef() == obj);
// now delete
shared.reset();
QVERIFY(weak.isNull());
}
safetyCheck();
{
// with two QWeakPointers
QObject *obj = new QObject;
QSharedPointer<QObject> shared(obj);
QWeakPointer<QObject> weak(shared);
{
QWeakPointer<QObject> weak2(shared);
QVERIFY(!weak2.isNull());
QVERIFY(weak == weak2);
}
QVERIFY(!weak.isNull());
shared.reset();
QVERIFY(weak.isNull());
}
safetyCheck();
{
// same, but delete the pointer while two QWeakPointers exist
QObject *obj = new QObject;
QSharedPointer<QObject> shared(obj);
QWeakPointer<QObject> weak(shared);
{
QWeakPointer<QObject> weak2(shared);
QVERIFY(!weak2.isNull());
shared.reset();
QVERIFY(weak.isNull());
QVERIFY(weak2.isNull());
}
QVERIFY(weak.isNull());
}
safetyCheck();
}
void tst_QSharedPointer::weakQObjectFromSharedPointer()
{
{
QSharedPointer<QObject> shared(new QObject);
QWeakPointer<QObject> weak = shared;
QVERIFY(!weak.isNull());
// delete:
shared.clear();
QVERIFY(weak.isNull());
}
}
void tst_QSharedPointer::objectCast()
{
{
OtherObject *data = new OtherObject;
QSharedPointer<QObject> baseptr = QSharedPointer<QObject>(data);
QVERIFY(baseptr == data);
QVERIFY(data == baseptr);
// perform object cast
QSharedPointer<OtherObject> ptr = qSharedPointerObjectCast<OtherObject>(baseptr);
QVERIFY(!ptr.isNull());
QCOMPARE(ptr.data(), data);
QVERIFY(ptr == data);
// again:
ptr = baseptr.objectCast<OtherObject>();
QVERIFY(ptr == data);
// again:
ptr = qobject_cast<OtherObject *>(baseptr);
QVERIFY(ptr == data);
// again:
ptr = qobject_cast<QSharedPointer<OtherObject> >(baseptr);
QVERIFY(ptr == data);
// again:
ptr = qobject_cast<OtherObject *>(std::move(baseptr));
QVERIFY(ptr == data);
QVERIFY(!baseptr);
}
safetyCheck();
{
const OtherObject *data = new OtherObject;
QSharedPointer<const QObject> baseptr = QSharedPointer<const QObject>(data);
QVERIFY(baseptr == data);
QVERIFY(data == baseptr);
// perform object cast
QSharedPointer<const OtherObject> ptr = qSharedPointerObjectCast<const OtherObject>(baseptr);
QVERIFY(!ptr.isNull());
QCOMPARE(ptr.data(), data);
QVERIFY(ptr == data);
// again:
ptr = baseptr.objectCast<const OtherObject>();
QVERIFY(ptr == data);
// again:
ptr = qobject_cast<const OtherObject *>(baseptr);
QVERIFY(ptr == data);
// again:
ptr = qobject_cast<QSharedPointer<const OtherObject> >(baseptr);
QVERIFY(ptr == data);
// again:
ptr = qobject_cast<const OtherObject *>(std::move(baseptr));
QVERIFY(ptr == data);
QVERIFY(!baseptr);
}
safetyCheck();
{
OtherObject *data = new OtherObject;
QSharedPointer<OtherObject> ptr = QSharedPointer<OtherObject>(data);
QWeakPointer<QObject> weakptr = ptr;
{
// perform object cast
QSharedPointer<OtherObject> otherptr = qSharedPointerObjectCast<OtherObject>(weakptr);
QVERIFY(otherptr == ptr);
// again:
otherptr = qobject_cast<OtherObject *>(weakptr);
QVERIFY(otherptr == ptr);
// again:
otherptr = qobject_cast<QSharedPointer<OtherObject> >(weakptr);
QVERIFY(otherptr == ptr);
}
// drop the reference:
ptr.clear();
QVERIFY(ptr.isNull());
QVERIFY(weakptr.toStrongRef().isNull());
QVERIFY(weakptr.lock().isNull());
// verify that the object casts fail without crash
QSharedPointer<OtherObject> otherptr = qSharedPointerObjectCast<OtherObject>(weakptr);
QVERIFY(otherptr.isNull());
// again:
otherptr = qobject_cast<OtherObject *>(weakptr);
QVERIFY(otherptr.isNull());
// again:
otherptr = qobject_cast<QSharedPointer<OtherObject> >(weakptr);
QVERIFY(otherptr.isNull());
}
safetyCheck();
}
void tst_QSharedPointer::objectCastFailureNoLeak()
{
// verify that a failing object cast doesn't keep the original object alive
auto ptr = QSharedPointer<QObject>::create();
auto qptr = QPointer(ptr.data());
auto ptr2 = ptr.objectCast<tst_QSharedPointer>();
auto ptr3 = std::move(ptr).objectCast<tst_QSharedPointer>();
QVERIFY(ptr);
QVERIFY(qptr);
QVERIFY(!ptr2);
QVERIFY(!ptr3);
ptr.reset();
QVERIFY(!ptr);
QVERIFY(!qptr);
}
void tst_QSharedPointer::objectCastStdSharedPtr()
{
{
OtherObject *data = new OtherObject;
std::shared_ptr<QObject> baseptr = std::shared_ptr<QObject>(data);
QVERIFY(baseptr.get() == data);
// perform successful object cast
std::shared_ptr<OtherObject> ptr = qobject_pointer_cast<OtherObject>(baseptr);
QVERIFY(ptr.get());
QVERIFY(ptr.get() == data);
QVERIFY(baseptr.get() == data);
}
{
OtherObject *data = new OtherObject;
std::shared_ptr<QObject> baseptr = std::shared_ptr<QObject>(data);
QVERIFY(baseptr.get() == data);
// perform successful object cast
std::shared_ptr<OtherObject> ptr = qobject_pointer_cast<OtherObject>(std::move(baseptr));
QVERIFY(ptr.get());
QVERIFY(ptr.get() == data);
QVERIFY(!baseptr.get());
}
{
QObject *data = new QObject;
std::shared_ptr<QObject> baseptr = std::shared_ptr<QObject>(data);
QVERIFY(baseptr.get() == data);
// perform unsuccessful object cast
std::shared_ptr<OtherObject> ptr = qobject_pointer_cast<OtherObject>(baseptr);
QVERIFY(!ptr.get());
QVERIFY(baseptr.get() == data);
}
{
QObject *data = new QObject;
std::shared_ptr<QObject> baseptr = std::shared_ptr<QObject>(data);
QVERIFY(baseptr.get() == data);
// perform unsuccessful object cast
std::shared_ptr<OtherObject> ptr = qobject_pointer_cast<OtherObject>(std::move(baseptr));
QVERIFY(!ptr.get());
QVERIFY(baseptr.get() == data);
}
}
void tst_QSharedPointer::differentPointers()
{
{
DiffPtrDerivedData *aData = new DiffPtrDerivedData;
Data *aBase = aData;
// ensure that this compiler isn't broken
if (*reinterpret_cast<quintptr *>(&aData) == *reinterpret_cast<quintptr *>(&aBase))
QFAIL("Something went very wrong -- we couldn't create two different pointers to the same object");
if (aData != aBase || aBase != aData)
QSKIP("Broken compiler");
QSharedPointer<DiffPtrDerivedData> ptr = QSharedPointer<DiffPtrDerivedData>(aData);
QSharedPointer<Data> baseptr = qSharedPointerStaticCast<Data>(ptr);
qDebug("naked: orig: %p; base: %p (%s) -- QSharedPointer: orig: %p; base %p (%s) -- result: %s",
aData, aBase, aData == aBase ? "equal" : "not equal",
ptr.data(), baseptr.data(), ptr.data() == baseptr.data() ? "equal" : "not equal",
baseptr.data() == aData ? "equal" : "not equal");
QVERIFY(ptr.data() == baseptr.data());
QVERIFY(baseptr.data() == ptr.data());
QVERIFY(ptr == baseptr);
QVERIFY(baseptr == ptr);
QVERIFY(ptr.data() == aBase);
QVERIFY(aBase == ptr.data());
QVERIFY(ptr.data() == aData);
QVERIFY(aData == ptr.data());
QVERIFY(ptr == aBase);
QVERIFY(aBase == ptr);
QVERIFY(ptr == aData);
QVERIFY(aData == ptr);
QVERIFY(baseptr.data() == aBase);
QVERIFY(aBase == baseptr.data());
QVERIFY(baseptr == aBase);
QVERIFY(aBase == baseptr);
QVERIFY(baseptr.data() == aData);
QVERIFY(aData == baseptr.data());
QVERIFY(baseptr == aData);
QVERIFY(aData == baseptr);
}
safetyCheck();
{
DiffPtrDerivedData *aData = new DiffPtrDerivedData;
Data *aBase = aData;
QVERIFY(aData == aBase);
QVERIFY(*reinterpret_cast<quintptr *>(&aData) != *reinterpret_cast<quintptr *>(&aBase));
QSharedPointer<Data> baseptr = QSharedPointer<Data>(aData);
QSharedPointer<DiffPtrDerivedData> ptr = qSharedPointerStaticCast<DiffPtrDerivedData>(baseptr);
QVERIFY(ptr == baseptr);
QVERIFY(ptr.data() == baseptr.data());
QVERIFY(ptr == aBase);
QVERIFY(baseptr == aData);
}
safetyCheck();
{
DiffPtrDerivedData *aData = new DiffPtrDerivedData;
Data *aBase = aData;
QVERIFY(aData == aBase);
QVERIFY(*reinterpret_cast<quintptr *>(&aData) != *reinterpret_cast<quintptr *>(&aBase));
QSharedPointer<DiffPtrDerivedData> ptr = QSharedPointer<DiffPtrDerivedData>(aData);
QSharedPointer<Data> baseptr = ptr;
QVERIFY(ptr == baseptr);
QVERIFY(ptr.data() == baseptr.data());
QVERIFY(ptr == aBase);
QVERIFY(ptr == aData);
QVERIFY(baseptr == aData);
QVERIFY(baseptr == aBase);
}
safetyCheck();
}
void tst_QSharedPointer::virtualBaseDifferentPointers()
{
{
VirtualDerived *aData = new VirtualDerived;
Data *aBase = aData;
QVERIFY(aData == aBase);
QVERIFY(*reinterpret_cast<quintptr *>(&aData) != *reinterpret_cast<quintptr *>(&aBase));
QSharedPointer<VirtualDerived> ptr = QSharedPointer<VirtualDerived>(aData);
QSharedPointer<Data> baseptr = qSharedPointerStaticCast<Data>(ptr);
QVERIFY(ptr == baseptr);
QVERIFY(ptr.data() == baseptr.data());
QVERIFY(ptr == aBase);
QVERIFY(ptr == aData);
QVERIFY(baseptr == aData);
QVERIFY(baseptr == aBase);
}
safetyCheck();
{
VirtualDerived *aData = new VirtualDerived;
Data *aBase = aData;
QVERIFY(aData == aBase);
QVERIFY(*reinterpret_cast<quintptr *>(&aData) != *reinterpret_cast<quintptr *>(&aBase));
QSharedPointer<VirtualDerived> ptr = QSharedPointer<VirtualDerived>(aData);
QSharedPointer<Data> baseptr = ptr;
QVERIFY(ptr == baseptr);
QVERIFY(ptr.data() == baseptr.data());
QVERIFY(ptr == aBase);
QVERIFY(ptr == aData);
QVERIFY(baseptr == aData);
QVERIFY(baseptr == aBase);
}
safetyCheck();
{
VirtualDerived *aData = new VirtualDerived;
QSharedPointer<VirtualDerived> ptr = QSharedPointer<VirtualDerived>(aData);
QWeakPointer<VirtualDerived> wptr = ptr;
ptr.reset();
QVERIFY(wptr.toStrongRef().isNull());
QWeakPointer<Data> wptr2 = wptr;
QVERIFY(wptr2.toStrongRef().isNull());
QWeakPointer<Data> wptr3 = std::move(wptr);
QVERIFY(wptr3.toStrongRef().isNull());
}
safetyCheck();
}
void tst_QSharedPointer::virtualBaseWeakPointerConversions()
{
struct Base { virtual ~Base() = default; };
struct Derived : virtual Base {};
{
QSharedPointer<Derived> d(new Derived);
QSharedPointer<const Base> cb = d;
QCOMPARE(cb, d);
QCOMPARE(cb.get(), d.get());
}
safetyCheck();
{
QSharedPointer<Derived> d(new Derived);
QWeakPointer<const Base> wcb = d;
QCOMPARE(wcb, d);
QCOMPARE(wcb.lock().get(), d.get());
}
safetyCheck();
{
QSharedPointer<Derived> d(new Derived);
QWeakPointer<Derived> wd = d;
QCOMPARE(wd, d);
QCOMPARE(wd.lock().get(), d.get());
QWeakPointer<const Base> wcb = wd;
QCOMPARE(wcb, wd);
QCOMPARE(wcb.lock().get(), d.get());
}
safetyCheck();
{
auto raw = new Derived;
QSharedPointer<Derived> d(raw);
QSharedPointer<const Base> cb = std::move(d);
QCOMPARE(d, nullptr);
QCOMPARE(cb.get(), raw);
}
safetyCheck();
{
QSharedPointer<Derived> d(new Derived);
QWeakPointer<const Base> wcb = std::move(d);
QCOMPARE(wcb, d);
QCOMPARE(wcb.lock().get(), d.get());
}
safetyCheck();
{
QSharedPointer<Derived> d(new Derived);
QWeakPointer<Derived> wd = std::move(d);
QCOMPARE(wd, d);
QCOMPARE(wd.lock().get(), d.get());
QWeakPointer<const Base> wcb = std::move(wd);
QCOMPARE(wd, nullptr);
QCOMPARE(wcb.lock().get(), d.get());
}
safetyCheck();
}
#ifndef QTEST_NO_RTTI
void tst_QSharedPointer::dynamicCast()
{
DerivedData *aData = new DerivedData;
QSharedPointer<Data> baseptr = QSharedPointer<Data>(aData);
{
QSharedPointer<DerivedData> derivedptr = qSharedPointerDynamicCast<DerivedData>(baseptr);
QVERIFY(baseptr == derivedptr);
QCOMPARE(derivedptr.data(), aData);
QCOMPARE(static_cast<Data *>(derivedptr.data()), baseptr.data());
}
QCOMPARE(int(refCountData(baseptr)->weakref.loadRelaxed()), 1);
QCOMPARE(int(refCountData(baseptr)->strongref.loadRelaxed()), 1);
{
QWeakPointer<Data> weakptr = baseptr;
QSharedPointer<DerivedData> derivedptr = qSharedPointerDynamicCast<DerivedData>(weakptr);
QVERIFY(baseptr == derivedptr);
QCOMPARE(derivedptr.data(), aData);
QCOMPARE(static_cast<Data *>(derivedptr.data()), baseptr.data());
}
QCOMPARE(int(refCountData(baseptr)->weakref.loadRelaxed()), 1);
QCOMPARE(int(refCountData(baseptr)->strongref.loadRelaxed()), 1);
{
QSharedPointer<DerivedData> derivedptr = baseptr.dynamicCast<DerivedData>();
QVERIFY(baseptr == derivedptr);
QCOMPARE(derivedptr.data(), aData);
QCOMPARE(static_cast<Data *>(derivedptr.data()), baseptr.data());
}
QCOMPARE(int(refCountData(baseptr)->weakref.loadRelaxed()), 1);
QCOMPARE(int(refCountData(baseptr)->strongref.loadRelaxed()), 1);
{
QSharedPointer<DerivedData> derivedptr = std::move(baseptr).dynamicCast<DerivedData>();
QCOMPARE(derivedptr.data(), aData);
}
QVERIFY(!baseptr);
}
void tst_QSharedPointer::dynamicCastDifferentPointers()
{
// DiffPtrDerivedData derives from both Data and Stuffing
DiffPtrDerivedData *aData = new DiffPtrDerivedData;
QSharedPointer<Data> baseptr = QSharedPointer<Data>(aData);
{
QSharedPointer<DiffPtrDerivedData> derivedptr = qSharedPointerDynamicCast<DiffPtrDerivedData>(baseptr);
QVERIFY(baseptr == derivedptr);
QCOMPARE(derivedptr.data(), aData);
QCOMPARE(static_cast<Data *>(derivedptr.data()), baseptr.data());
}
QCOMPARE(int(refCountData(baseptr)->weakref.loadRelaxed()), 1);
QCOMPARE(int(refCountData(baseptr)->strongref.loadRelaxed()), 1);
{
QWeakPointer<Data> weakptr = baseptr;
QSharedPointer<DiffPtrDerivedData> derivedptr = qSharedPointerDynamicCast<DiffPtrDerivedData>(weakptr);
QVERIFY(baseptr == derivedptr);
QCOMPARE(derivedptr.data(), aData);
QCOMPARE(static_cast<Data *>(derivedptr.data()), baseptr.data());
}
QCOMPARE(int(refCountData(baseptr)->weakref.loadRelaxed()), 1);
QCOMPARE(int(refCountData(baseptr)->strongref.loadRelaxed()), 1);
{
QSharedPointer<DiffPtrDerivedData> derivedptr = baseptr.dynamicCast<DiffPtrDerivedData>();
QVERIFY(baseptr == derivedptr);
QCOMPARE(derivedptr.data(), aData);
QCOMPARE(static_cast<Data *>(derivedptr.data()), baseptr.data());
}
QCOMPARE(int(refCountData(baseptr)->weakref.loadRelaxed()), 1);
QCOMPARE(int(refCountData(baseptr)->strongref.loadRelaxed()), 1);
{
Stuffing *nakedptr = dynamic_cast<Stuffing *>(baseptr.data());
QVERIFY(nakedptr);
QSharedPointer<Stuffing> otherbaseptr = qSharedPointerDynamicCast<Stuffing>(baseptr);
QVERIFY(!otherbaseptr.isNull());
QVERIFY(otherbaseptr == nakedptr);
QCOMPARE(otherbaseptr.data(), nakedptr);
QCOMPARE(static_cast<DiffPtrDerivedData*>(otherbaseptr.data()), aData);
}
{
QSharedPointer<DiffPtrDerivedData> derivedptr = std::move(baseptr).dynamicCast<DiffPtrDerivedData>();
QCOMPARE(derivedptr.data(), aData);
}
QVERIFY(!baseptr);
}
void tst_QSharedPointer::dynamicCastVirtualBase()
{
VirtualDerived *aData = new VirtualDerived;
QSharedPointer<Data> baseptr = QSharedPointer<Data>(aData);
{
QSharedPointer<VirtualDerived> derivedptr = qSharedPointerDynamicCast<VirtualDerived>(baseptr);
QVERIFY(baseptr == derivedptr);
QCOMPARE(derivedptr.data(), aData);
QCOMPARE(static_cast<Data *>(derivedptr.data()), baseptr.data());
}
QCOMPARE(int(refCountData(baseptr)->weakref.loadRelaxed()), 1);
QCOMPARE(int(refCountData(baseptr)->strongref.loadRelaxed()), 1);
{
QWeakPointer<Data> weakptr = baseptr;
QSharedPointer<VirtualDerived> derivedptr = qSharedPointerDynamicCast<VirtualDerived>(weakptr);
QVERIFY(baseptr == derivedptr);
QCOMPARE(derivedptr.data(), aData);
QCOMPARE(static_cast<Data *>(derivedptr.data()), baseptr.data());
}
QCOMPARE(int(refCountData(baseptr)->weakref.loadRelaxed()), 1);
QCOMPARE(int(refCountData(baseptr)->strongref.loadRelaxed()), 1);
{
QSharedPointer<VirtualDerived> derivedptr = baseptr.dynamicCast<VirtualDerived>();
QVERIFY(baseptr == derivedptr);
QCOMPARE(derivedptr.data(), aData);
QCOMPARE(static_cast<Data *>(derivedptr.data()), baseptr.data());
}
QCOMPARE(int(refCountData(baseptr)->weakref.loadRelaxed()), 1);
QCOMPARE(int(refCountData(baseptr)->strongref.loadRelaxed()), 1);
}
void tst_QSharedPointer::dynamicCastFailure()
{
QSharedPointer<Data> baseptr = QSharedPointer<Data>(new Data);
QVERIFY(dynamic_cast<DerivedData *>(baseptr.data()) == 0);
{
QSharedPointer<DerivedData> derivedptr = qSharedPointerDynamicCast<DerivedData>(baseptr);
QVERIFY(derivedptr.isNull());
}
QCOMPARE(int(refCountData(baseptr)->weakref.loadRelaxed()), 1);
QCOMPARE(int(refCountData(baseptr)->strongref.loadRelaxed()), 1);
{
QSharedPointer<DerivedData> derivedptr = baseptr.dynamicCast<DerivedData>();
QVERIFY(derivedptr.isNull());
}
QCOMPARE(int(refCountData(baseptr)->weakref.loadRelaxed()), 1);
QCOMPARE(int(refCountData(baseptr)->strongref.loadRelaxed()), 1);
}
void tst_QSharedPointer::dynamicCastFailureNoLeak()
{
NonTracked::dynamicCastFailureNoLeak();
}
#endif
void tst_QSharedPointer::constCorrectness()
{
{
QSharedPointer<Data> ptr = QSharedPointer<Data>(new Data);
QSharedPointer<const Data> cptr(ptr);
QSharedPointer<volatile Data> vptr(ptr);
cptr = ptr;
vptr = ptr;
ptr = qSharedPointerConstCast<Data>(cptr);
ptr = qSharedPointerConstCast<Data>(vptr);
ptr = cptr.constCast<Data>();
ptr = vptr.constCast<Data>();
QSharedPointer<const volatile Data> cvptr(ptr);
QSharedPointer<const volatile Data> cvptr2(cptr);
QSharedPointer<const volatile Data> cvptr3(vptr);
cvptr = ptr;
cvptr2 = cptr;
cvptr3 = vptr;
ptr = qSharedPointerConstCast<Data>(cvptr);
ptr = cvptr.constCast<Data>();
}
safetyCheck();
{
Data *aData = new Data;
QSharedPointer<Data> ptr = QSharedPointer<Data>(aData);
const QSharedPointer<Data> cptr = ptr;
ptr = cptr;
QSharedPointer<Data> other = qSharedPointerStaticCast<Data>(cptr);
#ifndef QT_NO_DYNAMIC_CAST
other = qSharedPointerDynamicCast<Data>(cptr);
#endif
QCOMPARE(cptr.data(), aData);
QCOMPARE(cptr.operator->(), aData);
}
safetyCheck();
}
static int customDeleterFnCallCount;
void customDeleterFn(Data *ptr)
{
++customDeleterFnCallCount;
delete ptr;
}
static int refcount;
template <typename T>
struct CustomDeleter
{
CustomDeleter() { ++refcount; }
CustomDeleter(const CustomDeleter &) { ++refcount; }
~CustomDeleter() { --refcount; }
inline void operator()(T *ptr)
{
delete ptr;
++callCount;
}
static int callCount;
};
template<typename T> int CustomDeleter<T>::callCount = 0;
void tst_QSharedPointer::customDeleter()
{
{
QSharedPointer<Data> ptr(0, &Data::doDelete);
QSharedPointer<Data> ptr2(0, &Data::alsoDelete);
QSharedPointer<Data> ptr3(0, &Data::virtualDelete);
}
safetyCheck();
{
QSharedPointer<Data> ptr(nullptr, &Data::doDelete);
QSharedPointer<Data> ptr2(nullptr, &Data::alsoDelete);
QSharedPointer<Data> ptr3(nullptr, &Data::virtualDelete);
}
safetyCheck();
{
QSharedPointer<Data> ptr(new Data, &Data::doDelete);
QSharedPointer<Data> ptr2(new Data, &Data::alsoDelete);
QSharedPointer<Data> ptr3(new Data, &Data::virtualDelete);
}
safetyCheck();
{
QSharedPointer<DerivedData> ptr(new DerivedData, &Data::doDelete);
QSharedPointer<DerivedData> ptr2(new DerivedData, &Data::alsoDelete);
QSharedPointer<DerivedData> ptr3(new DerivedData, &Data::virtualDelete);
}
safetyCheck();
customDeleterFnCallCount = 0;
{
QSharedPointer<Data> ptr = QSharedPointer<Data>(new Data, customDeleterFn);
ptr.data();
QCOMPARE(customDeleterFnCallCount, 0);
}
QCOMPARE(customDeleterFnCallCount, 1);
safetyCheck();
customDeleterFnCallCount = 0;
{
QSharedPointer<Data> ptr = QSharedPointer<Data>(new Data, customDeleterFn);
QCOMPARE(customDeleterFnCallCount, 0);
ptr.clear();
QCOMPARE(customDeleterFnCallCount, 1);
}
QCOMPARE(customDeleterFnCallCount, 1);
safetyCheck();
customDeleterFnCallCount = 0;
{
QSharedPointer<Data> ptr = QSharedPointer<Data>(new Data, customDeleterFn);
QCOMPARE(customDeleterFnCallCount, 0);
ptr = QSharedPointer<Data>(new Data);
QCOMPARE(customDeleterFnCallCount, 1);
}
QCOMPARE(customDeleterFnCallCount, 1);
safetyCheck();
customDeleterFnCallCount = 0;
{
QSharedPointer<Data> ptr = QSharedPointer<Data>(new DerivedData, customDeleterFn);
ptr.data();
QCOMPARE(customDeleterFnCallCount, 0);
}
QCOMPARE(customDeleterFnCallCount, 1);
safetyCheck();
customDeleterFnCallCount = 0;
{
QSharedPointer<DerivedData> ptr = QSharedPointer<DerivedData>(new DerivedData, customDeleterFn);
ptr.data();
QCOMPARE(customDeleterFnCallCount, 0);
}
QCOMPARE(customDeleterFnCallCount, 1);
safetyCheck();
customDeleterFnCallCount = 0;
{
QSharedPointer<Data> other;
{
QSharedPointer<Data> ptr = QSharedPointer<Data>(new Data, customDeleterFn);
other = ptr;
QCOMPARE(customDeleterFnCallCount, 0);
}
QCOMPARE(customDeleterFnCallCount, 0);
}
QCOMPARE(customDeleterFnCallCount, 1);
safetyCheck();
customDeleterFnCallCount = 0;
{
QSharedPointer<Data> other;
{
QSharedPointer<DerivedData> ptr = QSharedPointer<DerivedData>(new DerivedData, customDeleterFn);
other = ptr;
QCOMPARE(customDeleterFnCallCount, 0);
}
QCOMPARE(customDeleterFnCallCount, 0);
}
QCOMPARE(customDeleterFnCallCount, 1);
safetyCheck();
refcount = 0;
CustomDeleter<Data> dataDeleter;
dataDeleter.callCount = 0;
{
QSharedPointer<Data> ptr = QSharedPointer<Data>(new Data, dataDeleter);
ptr.data();
QCOMPARE(dataDeleter.callCount, 0);
}
QCOMPARE(dataDeleter.callCount, 1);
QCOMPARE(refcount, 1);
safetyCheck();
dataDeleter.callCount = 0;
{
QSharedPointer<Data> ptr = QSharedPointer<Data>(new Data, dataDeleter);
QSharedPointer<Data> other = ptr;
other.clear();
QCOMPARE(dataDeleter.callCount, 0);
}
QCOMPARE(dataDeleter.callCount, 1);
QCOMPARE(refcount, 1);
safetyCheck();
dataDeleter.callCount = 0;
{
QSharedPointer<Data> other;
{
QSharedPointer<Data> ptr = QSharedPointer<Data>(new Data, dataDeleter);
other = ptr;
QCOMPARE(dataDeleter.callCount, 0);
}
QCOMPARE(dataDeleter.callCount, 0);
}
QCOMPARE(dataDeleter.callCount, 1);
QCOMPARE(refcount, 1);
safetyCheck();
dataDeleter.callCount = 0;
{
QSharedPointer<DerivedData> ptr = QSharedPointer<DerivedData>(new DerivedData, dataDeleter);
ptr.data();
QCOMPARE(dataDeleter.callCount, 0);
}
QCOMPARE(dataDeleter.callCount, 1);
QCOMPARE(refcount, 1);
safetyCheck();
CustomDeleter<DerivedData> derivedDataDeleter;
derivedDataDeleter.callCount = 0;
dataDeleter.callCount = 0;
{
QSharedPointer<DerivedData> ptr = QSharedPointer<DerivedData>(new DerivedData, derivedDataDeleter);
ptr.data();
QCOMPARE(dataDeleter.callCount, 0);
QCOMPARE(derivedDataDeleter.callCount, 0);
}
QCOMPARE(dataDeleter.callCount, 0);
QCOMPARE(derivedDataDeleter.callCount, 1);
QCOMPARE(refcount, 2);
safetyCheck();
derivedDataDeleter.callCount = 0;
dataDeleter.callCount = 0;
{
QSharedPointer<Data> other;
{
QSharedPointer<DerivedData> ptr = QSharedPointer<DerivedData>(new DerivedData, dataDeleter);
other = ptr;
QCOMPARE(dataDeleter.callCount, 0);
QCOMPARE(derivedDataDeleter.callCount, 0);
}
QCOMPARE(dataDeleter.callCount, 0);
QCOMPARE(derivedDataDeleter.callCount, 0);
}
QCOMPARE(dataDeleter.callCount, 1);
QCOMPARE(derivedDataDeleter.callCount, 0);
QCOMPARE(refcount, 2);
safetyCheck();
derivedDataDeleter.callCount = 0;
dataDeleter.callCount = 0;
{
QSharedPointer<Data> other;
{
QSharedPointer<DerivedData> ptr = QSharedPointer<DerivedData>(new DerivedData, derivedDataDeleter);
other = ptr;
QCOMPARE(dataDeleter.callCount, 0);
QCOMPARE(derivedDataDeleter.callCount, 0);
}
QCOMPARE(dataDeleter.callCount, 0);
QCOMPARE(derivedDataDeleter.callCount, 0);
}
QCOMPARE(dataDeleter.callCount, 0);
QCOMPARE(derivedDataDeleter.callCount, 1);
QCOMPARE(refcount, 2);
safetyCheck();
CustomDeleter<NVDData> nvdeleter;
nvdeleter.callCount = 0;
{
QSharedPointer<NVDData> ptr(new NVDData, nvdeleter);
}
QCOMPARE(nvdeleter.callCount, 1);
safetyCheck();
CustomDeleter<NVDDerivedData> nvderiveddeleter;
nvdeleter.callCount = 0;
nvderiveddeleter.callCount = 0;
{
QSharedPointer<NVDDerivedData> ptr(new NVDDerivedData, nvderiveddeleter);
}
QCOMPARE(nvdeleter.callCount, 0);
QCOMPARE(nvderiveddeleter.callCount, 1);
safetyCheck();
nvdeleter.callCount = 0;
nvderiveddeleter.callCount = 0;
{
QSharedPointer<NVDData> ptr(new NVDDerivedData, nvderiveddeleter);
}
QCOMPARE(nvdeleter.callCount, 0);
QCOMPARE(nvderiveddeleter.callCount, 1);
safetyCheck();
// a custom deleter with a different pointer parameter
{
QSharedPointer<char> ptr(static_cast<char *>(malloc(1)), free);
}
safetyCheck();
}
// The compiler needs to be in C++11 mode and to support lambdas
void tst_QSharedPointer::lambdaCustomDeleter()
{
{
// stateless, one-argument
QSharedPointer<Data> ptr(new Data, [](Data *d) { delete d; });
QSharedPointer<Data> ptr2(new Data, [](Data *d) { d->doDelete(); });
}
safetyCheck();
customDeleterFnCallCount = 0;
{
// stateless, one-argument, modifies globals
QSharedPointer<Data> ptr(new Data, [](Data *d) { ++customDeleterFnCallCount; delete d; });
}
safetyCheck();
QCOMPARE(customDeleterFnCallCount, 1);
{
// stateful by ref, one-argument
int i = 0;
QSharedPointer<Data> ptr(new Data, [&i](Data *d) { i = 42; delete d; });
ptr.clear();
QCOMPARE(i, 42);
}
safetyCheck();
}
void tst_QSharedPointer::customDeleterOnNullptr()
{
Data *null = nullptr;
int callCount = 0;
auto deleter = [&callCount](Data *) { ++callCount; };
{
QSharedPointer<Data> ptr(null, deleter);
}
safetyCheck();
QCOMPARE(callCount, 1);
callCount = 0;
{
QSharedPointer<Data> ptr(nullptr, deleter);
}
safetyCheck();
QCOMPARE(callCount, 1);
callCount = 0;
{
QSharedPointer<Data> ptr1(null, deleter);
QSharedPointer<Data> ptr2(nullptr, deleter);
}
safetyCheck();
QCOMPARE(callCount, 2);
}
void customQObjectDeleterFn(QObject *obj)
{
++customDeleterFnCallCount;
delete obj;
}
void tst_QSharedPointer::creating()
{
Data::generationCounter = Data::destructorCounter = 0;
{
QSharedPointer<Data> ptr = QSharedPointer<Data>::create();
QVERIFY(ptr.data());
QCOMPARE(Data::generationCounter, 1);
QCOMPARE(ptr->generation, 1);
QCOMPARE(Data::destructorCounter, 0);
QCOMPARE(ptr->classLevel(), 1);
ptr.clear();
QCOMPARE(Data::destructorCounter, 1);
}
safetyCheck();
Data::generationCounter = Data::destructorCounter = 0;
{
QSharedPointer<Data> ptr = QSharedPointer<Data>::create();
QWeakPointer<Data> weakptr = ptr;
QtSharedPointer::ExternalRefCountData *d = refCountData(ptr);
ptr.clear();
QVERIFY(ptr.isNull());
QCOMPARE(Data::destructorCounter, 1);
// valgrind will complain here if something happened to the pointer
QVERIFY(d->weakref.loadRelaxed() == 1);
QVERIFY(d->strongref.loadRelaxed() == 0);
}
safetyCheck();
Data::generationCounter = Data::destructorCounter = 0;
DerivedData::derivedDestructorCounter = 0;
{
QSharedPointer<Data> ptr = QSharedPointer<DerivedData>::create();
QCOMPARE(ptr->classLevel(), 2);
QCOMPARE(ptr.staticCast<DerivedData>()->moreData, 0);
ptr.clear();
QCOMPARE(Data::destructorCounter, 1);
QCOMPARE(DerivedData::derivedDestructorCounter, 1);
}
safetyCheck();
{
QSharedPointer<Data> ptr = QSharedPointer<DiffPtrDerivedData>::create();
QCOMPARE(ptr->classLevel(), 3);
QCOMPARE(ptr.staticCast<DiffPtrDerivedData>()->buffer[7]+0, 16-7);
QCOMPARE(ptr.staticCast<DiffPtrDerivedData>()->buffer[3]+0, 16-3);
QCOMPARE(ptr.staticCast<DiffPtrDerivedData>()->buffer[0]+0, 16);
}
safetyCheck();
{
QSharedPointer<VirtualDerived> ptr = QSharedPointer<VirtualDerived>::create();
QCOMPARE(ptr->classLevel(), 4);
QCOMPARE(ptr->moreData, 0xc0ffee);
QSharedPointer<Data> baseptr = ptr;
QCOMPARE(baseptr->classLevel(), 4);
}
safetyCheck();
}
void tst_QSharedPointer::creatingCvQualified()
{
auto cptr = QSharedPointer<const Data>::create();
auto vptr = QSharedPointer<volatile Data>::create();
auto cvptr = QSharedPointer<const volatile Data>::create();
}
void tst_QSharedPointer::creatingVariadic()
{
int i = 42;
{
NoDefaultConstructor1(1); // control check
QSharedPointer<NoDefaultConstructor1> ptr = QSharedPointer<NoDefaultConstructor1>::create(1);
QCOMPARE(ptr->i, 1);
NoDefaultConstructor1(0u); // control check
ptr = QSharedPointer<NoDefaultConstructor1>::create(0u);
QCOMPARE(ptr->i, 42);
NoDefaultConstructor1 x(i); // control check
ptr = QSharedPointer<NoDefaultConstructor1>::create(i);
QCOMPARE(ptr->i, i);
}
{
NoDefaultConstructor2((void*)0, 1); // control check
QSharedPointer<NoDefaultConstructor2> ptr = QSharedPointer<NoDefaultConstructor2>::create((void*)0, 1);
QCOMPARE(ptr->i, 1);
QCOMPARE(ptr->ptr, (void*)0);
int *null = 0;
NoDefaultConstructor2(null, 2); // control check
ptr = QSharedPointer<NoDefaultConstructor2>::create(null, 2);
QCOMPARE(ptr->i, 2);
QCOMPARE(ptr->ptr, (void*)0);
NoDefaultConstructor2(nullptr, 3); // control check
ptr = QSharedPointer<NoDefaultConstructor2>::create(nullptr, 3);
QCOMPARE(ptr->i, 3);
QCOMPARE(ptr->ptr, (void*)nullptr);
}
{
NoDefaultConstructorRef1 x(i); // control check
QSharedPointer<NoDefaultConstructorRef1> ptr = QSharedPointer<NoDefaultConstructorRef1>::create(i);
QCOMPARE(ptr->i, i);
QCOMPARE(&ptr->i, &i);
}
{
NoDefaultConstructorRRef1(std::move(i)); // control check
QSharedPointer<NoDefaultConstructorRRef1> ptr = QSharedPointer<NoDefaultConstructorRRef1>::create(std::move(i));
QCOMPARE(ptr->i, i);
}
{
NoDefaultConstructorRRef2(std::unique_ptr<int>(new int(1))); // control check
QSharedPointer<NoDefaultConstructorRRef2> ptr = QSharedPointer<NoDefaultConstructorRRef2>::create(std::unique_ptr<int>(new int(1)));
QCOMPARE(*ptr->i, 1);
std::unique_ptr<int> p(new int(i));
ptr = QSharedPointer<NoDefaultConstructorRRef2>::create(std::move(p));
QCOMPARE(*ptr->i, i);
}
{
QString text("Hello, World");
NoDefaultConstructorRef2(text, 1); // control check
QSharedPointer<NoDefaultConstructorRef2> ptr = QSharedPointer<NoDefaultConstructorRef2>::create(text, 1);
QCOMPARE(ptr->str, text);
QCOMPARE(ptr->i, 1);
}
{
QSharedPointer<NoDefaultConstructorConstRef2> ptr;
NoDefaultConstructorConstRef2(QLatin1String("string"), 1); // control check
ptr = QSharedPointer<NoDefaultConstructorConstRef2>::create(QLatin1String("string"), 1);
QCOMPARE(ptr->str, QString("string"));
QCOMPARE(ptr->i, 1);
NoDefaultConstructorConstRef2(QByteArray("bytearray")); // control check
ptr = QSharedPointer<NoDefaultConstructorConstRef2>::create(QByteArray("bytearray"));
QCOMPARE(ptr->str, QString("bytearray"));
QCOMPARE(ptr->i, 42);
}
}
void tst_QSharedPointer::creatingQObject()
{
{
QSharedPointer<QObject> ptr = QSharedPointer<QObject>::create();
QCOMPARE(ptr->metaObject(), &QObject::staticMetaObject);
QPointer<QObject> qptr = ptr.data();
ptr.clear();
QVERIFY(qptr.isNull());
}
safetyCheck();
{
QSharedPointer<QObject> ptr = QSharedPointer<OtherObject>::create();
QCOMPARE(ptr->metaObject(), &OtherObject::staticMetaObject);
}
safetyCheck();
}
void tst_QSharedPointer::mixTrackingPointerCode()
{
{
// pointer created with tracking
// deleted in code without tracking
QSharedPointer<int> ptr = QSharedPointer<int>(new int(42));
Wrapper w(ptr);
ptr.clear();
}
safetyCheck();
{
// pointer created without tracking
// deleted in code with tracking
Wrapper w = Wrapper::create();
w.ptr.clear();
}
}
class ThreadData
{
QAtomicInt * volatile ptr;
public:
ThreadData(QAtomicInt *p) : ptr(p) { }
~ThreadData() { QtPrivate::volatilePreIncrement(ptr); }
void ref()
{
// if we're called after the destructor, we'll crash
ptr->ref();
}
};
class StrongThread: public QThread
{
protected:
void run() override
{
sleep(std::chrono::microseconds{QRandomGenerator::global()->bounded(2000)});
ptr->ref();
ptr.clear();
}
public:
QSharedPointer<ThreadData> ptr;
};
class WeakThread: public QThread
{
protected:
void run() override
{
sleep(std::chrono::microseconds{QRandomGenerator::global()->bounded(2000)});
QSharedPointer<ThreadData> ptr = weak;
if (ptr)
ptr->ref();
ptr.clear();
}
public:
QWeakPointer<ThreadData> weak;
};
void tst_QSharedPointer::threadStressTest_data()
{
QTest::addColumn<int>("strongThreadCount");
QTest::addColumn<int>("weakThreadCount");
QTest::newRow("0+0") << 0 << 0;
QTest::newRow("1+0") << 1 << 0;
QTest::newRow("2+0") << 2 << 0;
QTest::newRow("10+0") << 10 << 0;
QTest::newRow("0+1") << 0 << 1;
QTest::newRow("1+1") << 1 << 1;
QTest::newRow("2+10") << 2 << 10;
QTest::newRow("5+10") << 5 << 10;
QTest::newRow("5+30") << 5 << 30;
QTest::newRow("100+100") << 100 << 100;
}
void tst_QSharedPointer::threadStressTest()
{
QFETCH(int, strongThreadCount);
QFETCH(int, weakThreadCount);
int guard1[128];
QAtomicInt counter;
int guard2[128];
memset(guard1, 0, sizeof guard1);
memset(guard2, 0, sizeof guard2);
for (int r = 0; r < 5; ++r) {
QList<QThread*> allThreads(6 * qMax(strongThreadCount, weakThreadCount) + 3, 0);
QSharedPointer<ThreadData> base = QSharedPointer<ThreadData>(new ThreadData(&counter));
counter.storeRelaxed(0);
// set the pointers
for (int i = 0; i < strongThreadCount; ++i) {
StrongThread *t = new StrongThread;
t->ptr = base;
allThreads[2 * i] = t;
}
for (int i = 0; i < weakThreadCount; ++i) {
WeakThread *t = new WeakThread;
t->weak = base;
allThreads[6 * i + 3] = t;
}
base.clear();
// start threads
for (int i = 0; i < allThreads.size(); ++i)
if (allThreads[i]) allThreads[i]->start();
// wait for them to finish
for (int i = 0; i < allThreads.size(); ++i)
if (allThreads[i]) allThreads[i]->wait();
qDeleteAll(allThreads);
// ensure the guards aren't touched
for (uint i = 0; i < sizeof guard1 / sizeof guard1[0]; ++i)
QVERIFY(!guard1[i]);
for (uint i = 0; i < sizeof guard2 / sizeof guard2[0]; ++i)
QVERIFY(!guard2[i]);
// verify that the count is the right range
int minValue = strongThreadCount;
int maxValue = strongThreadCount + weakThreadCount;
QVERIFY(counter.loadRelaxed() >= minValue);
QVERIFY(counter.loadRelaxed() <= maxValue);
}
}
template<typename Container, bool Ordered, bool Multi>
void hashAndMapTest()
{
typedef typename Container::key_type Key;
typedef typename Container::mapped_type Value;
Container c;
QVERIFY(c.isEmpty());
Key k0;
c.insert(k0, Value(0));
QVERIFY(!c.isEmpty());
typename Container::iterator it;
it = c.find(k0);
QVERIFY(it != c.end());
it = c.find(Key());
QVERIFY(it != c.end());
it = c.find(Key(0));
QVERIFY(it != c.end());
Key k1(new typename Key::value_type(42));
it = c.find(k1);
QVERIFY(it == c.end());
c.insert(k1, Value(42));
it = c.find(k1);
QVERIFY(it != c.end());
QVERIFY(it != c.find(Key()));
if (Ordered) {
QVERIFY(k0 < k1);
it = c.begin();
QCOMPARE(it.key(), k0);
QCOMPARE(it.value(), Value(0));
++it;
QCOMPARE(it.key(), k1);
QCOMPARE(it.value(), Value(42));
++it;
QVERIFY(it == c.end());
}
if (Multi) {
c.insert(k1, Value(47));
it = c.find(k1);
QVERIFY(it != c.end());
QCOMPARE(it.key(), k1);
++it;
QVERIFY(it != c.end());
QCOMPARE(it.key(), k1);
++it;
if (Ordered)
QVERIFY(it == c.end());
}
}
void tst_QSharedPointer::map()
{
hashAndMapTest<QMap<QSharedPointer<int>, int>, true, false>();
hashAndMapTest<QMultiMap<QSharedPointer<int>, int>, true, true>();
}
void tst_QSharedPointer::hash()
{
hashAndMapTest<QHash<QSharedPointer<int>, int>, false, false>();
hashAndMapTest<QMultiHash<QSharedPointer<int>, int>, false, true>();
}
void tst_QSharedPointer::validConstructs()
{
{
Data *aData = new Data;
QSharedPointer<Data> ptr1 = QSharedPointer<Data>(aData);
QT_WARNING_PUSH
QT_WARNING_DISABLE_CLANG("-Wself-assign-overloaded")
ptr1 = ptr1; // valid
QT_WARNING_POP
QSharedPointer<Data> ptr2(ptr1);
ptr1 = ptr2;
ptr2 = ptr1;
ptr1 = QSharedPointer<Data>();
ptr1 = ptr2;
}
}
#if 0
typedef bool (QTest::QExternalTest:: * TestFunction)(const QByteArray &body);
Q_DECLARE_METATYPE(TestFunction)
void tst_QSharedPointer::invalidConstructs_data()
{
QTest::addColumn<TestFunction>("testFunction");
QTest::addColumn<QString>("code");
QTest::newRow("sanity-checking") << &QTest::QExternalTest::tryCompile << "";
// QSharedPointer<void> is not allowed
QTest::newRow("void") << &QTest::QExternalTest::tryCompileFail << "QSharedPointer<void> ptr;";
// implicit initialization
QTest::newRow("implicit-initialization1")
<< &QTest::QExternalTest::tryCompileFail
<< "QSharedPointer<Data> ptr = new Data;";
QTest::newRow("implicit-initialization2")
<< &QTest::QExternalTest::tryCompileFail
<< "QSharedPointer<Data> ptr;"
"ptr = new Data;";
QTest::newRow("implicit-initialization3")
<< &QTest::QExternalTest::tryCompileFail
<< "QWeakPointer<Data> ptr = new Data;";
QTest::newRow("implicit-initialization4")
<< &QTest::QExternalTest::tryCompileFail
<< "QWeakPointer<Data> ptr;"
"ptr = new Data;";
// use of forward-declared class
QTest::newRow("creating-forward-declaration")
<< &QTest::QExternalTest::tryCompileFail
<< "QSharedPointer<ForwardDeclared>::create();";
// upcast without cast operator:
QTest::newRow("upcast1")
<< &QTest::QExternalTest::tryCompileFail
<< "QSharedPointer<Data> baseptr = QSharedPointer<Data>(new DerivedData);\n"
"QSharedPointer<DerivedData> ptr(baseptr);";
QTest::newRow("upcast2")
<< &QTest::QExternalTest::tryCompileFail
<< "QSharedPointer<Data> baseptr = QSharedPointer<Data>(new DerivedData);\n"
"QSharedPointer<DerivedData> ptr;\n"
"ptr = baseptr;";
// dropping of const
QTest::newRow("const-dropping1")
<< &QTest::QExternalTest::tryCompileFail
<< "QSharedPointer<const Data> baseptr = QSharedPointer<const Data>(new Data);\n"
"QSharedPointer<Data> ptr(baseptr);";
QTest::newRow("const-dropping2")
<< &QTest::QExternalTest::tryCompileFail
<< "QSharedPointer<const Data> baseptr = QSharedPointer<const Data>(new Data);\n"
"QSharedPointer<Data> ptr;"
"ptr = baseptr;";
QTest::newRow("const-dropping-static-cast")
<< &QTest::QExternalTest::tryCompileFail
<< "QSharedPointer<const Data> baseptr = QSharedPointer<const Data>(new Data);\n"
"qSharedPointerStaticCast<DerivedData>(baseptr);";
#ifndef QTEST_NO_RTTI
QTest::newRow("const-dropping-dynamic-cast")
<< &QTest::QExternalTest::tryCompileFail
<< "QSharedPointer<const Data> baseptr = QSharedPointer<const Data>(new Data);\n"
"qSharedPointerDynamicCast<DerivedData>(baseptr);";
#endif
QTest::newRow("const-dropping-object-cast1")
<< &QTest::QExternalTest::tryCompileFail
<< "QSharedPointer<const QObject> baseptr = QSharedPointer<const QObject>(new QObject);\n"
"qSharedPointerObjectCast<QCoreApplication>(baseptr);";
QTest::newRow("const-dropping-object-cast2")
<< &QTest::QExternalTest::tryCompileFail
<< "QSharedPointer<const QObject> baseptr = QSharedPointer<const QObject>(new QObject);\n"
"qobject_cast<QCoreApplication *>(baseptr);";
// arithmethics through automatic cast operators
QTest::newRow("arithmethic1")
<< &QTest::QExternalTest::tryCompileFail
<< "QSharedPointer<int> a;"
"QSharedPointer<Data> b;\n"
"if (a == b) return;";
QTest::newRow("arithmethic2")
<< &QTest::QExternalTest::tryCompileFail
<< "QSharedPointer<int> a;"
"QSharedPointer<Data> b;\n"
"if (a + b) return;";
// two objects with the same pointer
QTest::newRow("same-pointer")
<< &QTest::QExternalTest::tryRunFail
<< "Data *aData = new Data;\n"
"QSharedPointer<Data> ptr1 = QSharedPointer<Data>(aData);\n"
"QSharedPointer<Data> ptr2 = QSharedPointer<Data>(aData);\n";
// two QObjects with the same pointer
QTest::newRow("same-pointer-to-qobject")
<< &QTest::QExternalTest::tryRunFail
<< "QObject *anObj = new QObject;\n"
"QSharedPointer<QObject> ptr1 = QSharedPointer<QObject>(anObj);\n"
"QSharedPointer<QObject> ptr2 = QSharedPointer<QObject>(anObj);\n";
// re-creation:
QTest::newRow("re-creation")
<< &QTest::QExternalTest::tryRunFail
<< "Data *aData = new Data;\n"
"QSharedPointer<Data> ptr1 = QSharedPointer<Data>(aData);"
"ptr1 = QSharedPointer<Data>(aData);";
// any type of cast for unrelated types:
// (we have no reinterpret_cast)
QTest::newRow("invalid-cast1")
<< &QTest::QExternalTest::tryCompileFail
<< "QSharedPointer<Data> ptr1;\n"
"QSharedPointer<int> ptr2 = qSharedPointerStaticCast<int>(ptr1);";
#ifndef QTEST_NO_RTTI
QTest::newRow("invalid-cast2")
<< &QTest::QExternalTest::tryCompileFail
<< "QSharedPointer<Data> ptr1;\n"
"QSharedPointer<int> ptr2 = qSharedPointerDynamicCast<int>(ptr1);";
#endif
QTest::newRow("invalid-cast3")
<< &QTest::QExternalTest::tryCompileFail
<< "QSharedPointer<Data> ptr1;\n"
"QSharedPointer<int> ptr2 = qSharedPointerConstCast<int>(ptr1);";
QTest::newRow("invalid-cast4")
<< &QTest::QExternalTest::tryCompileFail
<< "QSharedPointer<Data> ptr1;\n"
"QSharedPointer<int> ptr2 = qSharedPointerObjectCast<int>(ptr1);";
QTest::newRow("weak-pointer-from-regular-pointer")
<< &QTest::QExternalTest::tryCompileFail
<< "Data *ptr = nullptr;\n"
"QWeakPointer<Data> weakptr(ptr);\n";
QTest::newRow("shared-pointer-implicit-from-uninitialized")
<< &QTest::QExternalTest::tryCompileFail
<< "Data *ptr = nullptr;\n"
"QSharedPointer<Data> weakptr = Qt::Uninitialized;\n";
QTest::newRow("incompatible-custom-deleter1")
<< &QTest::QExternalTest::tryCompileFail
<< "extern void incompatibleCustomDeleter(int *);\n"
"QSharedPointer<Data> ptr(new Data, incompatibleCustomDeleter);\n";
QTest::newRow("incompatible-custom-deleter2")
<< &QTest::QExternalTest::tryCompileFail
<< "struct IncompatibleCustomDeleter { void operator()(int *); };\n"
"QSharedPointer<Data> ptr(new Data, IncompatibleCustomDeleter());\n";
QTest::newRow("incompatible-custom-lambda-deleter")
<< &QTest::QExternalTest::tryCompileFail
<< "QSharedPointer<Data> ptr(new Data, [](int *) {});\n";
QTest::newRow("incompatible-overload")
<< &QTest::QExternalTest::tryCompileFail
<< "void foo(QSharedPointer<DerivedData>) {}\n"
"void bar() { foo(QSharedPointer<Data>()); }\n";
}
void tst_QSharedPointer::invalidConstructs()
{
#ifdef Q_CC_MINGW
QSKIP("The maintainer of QSharedPointer: 'We don't know what the problem is so skip the tests.'");
#endif
#ifdef QTEST_CROSS_COMPILED
QSKIP("This test does not work on cross compiled systems");
#endif
QTest::QExternalTest test;
test.setQtModules(QTest::QExternalTest::QtCore);
test.setProgramHeader(
"#define QT_SHAREDPOINTER_TRACK_POINTERS\n"
"#define QT_DEBUG\n"
"#include <QtCore/qsharedpointer.h>\n"
"#include <QtCore/qcoreapplication.h>\n"
"\n"
"struct Data { int i; };\n"
"struct DerivedData: public Data { int j; };\n"
"\n"
"class ForwardDeclared;\n"
);
QFETCH(QString, code);
static bool sane = true;
if (code.isEmpty()) {
if (!test.tryRun("")
|| !test.tryRunFail("exit(1);")
|| !test.tryRun("QSharedPointer<Data> baseptr; QSharedPointer<DerivedData> ptr;")) {
sane = false;
qWarning("Sanity checking failed\nCode:\n%s\n",
qPrintable(test.errorReport()));
}
}
if (!sane)
QFAIL("External testing failed sanity checking, cannot proceed");
QFETCH(TestFunction, testFunction);
QByteArray body = code.toLatin1();
bool result = (test.*testFunction)(body);
if (!result || qgetenv("QTEST_EXTERNAL_DEBUG").toInt() > 0) {
qDebug("External test output:");
#ifdef Q_CC_MSVC
// MSVC prints errors to stdout
printf("%s\n", test.standardOutput().constData());
#endif
printf("%s\n", test.standardError().constData());
}
if (!result) {
qWarning("External code testing failed\nCode:\n%s\n", body.constData());
QFAIL("Fail");
}
}
#endif // #if 0
void tst_QSharedPointer::qvariantCast()
{
QSharedPointer<QFile> sp = QSharedPointer<QFile>::create();
sp->setObjectName("A test name");
QVariant v = QVariant::fromValue(sp);
{
QSharedPointer<QObject> other = qSharedPointerFromVariant<QObject>(v);
QCOMPARE(other->objectName(), QString::fromLatin1("A test name"));
}
{
QSharedPointer<QIODevice> other = qSharedPointerFromVariant<QIODevice>(v);
QCOMPARE(other->objectName(), QString::fromLatin1("A test name"));
}
{
QSharedPointer<QFile> other = qSharedPointerFromVariant<QFile>(v);
QCOMPARE(other->objectName(), QString::fromLatin1("A test name"));
}
{
QSharedPointer<QThread> other = qSharedPointerFromVariant<QThread>(v);
QVERIFY(!other);
}
// Intentionally does not compile.
// QSharedPointer<int> sop = qSharedPointerFromVariant<int>(v);
}
class SomeClass : public QEnableSharedFromThis<SomeClass>
{
public:
SomeClass()
{
}
QSharedPointer<SomeClass> getSharedPtr()
{
return sharedFromThis();
}
QSharedPointer<const SomeClass> getSharedPtr() const
{
return sharedFromThis();
}
Data data;
};
void tst_QSharedPointer::sharedFromThis()
{
const int generations = Data::generationCounter;
const int destructions = Data::destructorCounter;
{
SomeClass sc;
QSharedPointer<SomeClass> scp = sc.sharedFromThis();
QVERIFY(scp.isNull());
QCOMPARE(Data::generationCounter, generations + 1);
QCOMPARE(Data::destructorCounter, destructions);
QSharedPointer<const SomeClass> const_scp = sc.sharedFromThis();
QVERIFY(const_scp.isNull());
QCOMPARE(Data::generationCounter, generations + 1);
QCOMPARE(Data::destructorCounter, destructions);
QWeakPointer<SomeClass> wcp = sc.sharedFromThis();
QVERIFY(wcp.isNull());
QCOMPARE(Data::generationCounter, generations + 1);
QCOMPARE(Data::destructorCounter, destructions);
QWeakPointer<const SomeClass> const_wcp = sc.sharedFromThis();
QVERIFY(const_wcp.isNull());
QCOMPARE(Data::generationCounter, generations + 1);
QCOMPARE(Data::destructorCounter, destructions);
}
QCOMPARE(Data::generationCounter, generations + 1);
QCOMPARE(Data::destructorCounter, destructions + 1);
{
const SomeClass sc;
QSharedPointer<const SomeClass> const_scp = sc.sharedFromThis();
QVERIFY(const_scp.isNull());
QCOMPARE(Data::generationCounter, generations + 2);
QCOMPARE(Data::destructorCounter, destructions + 1);
QWeakPointer<const SomeClass> const_wcp = sc.sharedFromThis();
QVERIFY(const_wcp.isNull());
QCOMPARE(Data::generationCounter, generations + 2);
QCOMPARE(Data::destructorCounter, destructions + 1);
}
QCOMPARE(Data::generationCounter, generations + 2);
QCOMPARE(Data::destructorCounter, destructions + 2);
{
SomeClass *sc = new SomeClass;
QCOMPARE(Data::generationCounter, generations + 3);
QCOMPARE(Data::destructorCounter, destructions + 2);
QSharedPointer<SomeClass> scp;
QVERIFY(scp.isNull());
QCOMPARE(Data::generationCounter, generations + 3);
QCOMPARE(Data::destructorCounter, destructions + 2);
scp = sc->sharedFromThis();
QVERIFY(scp.isNull());
QCOMPARE(Data::generationCounter, generations + 3);
QCOMPARE(Data::destructorCounter, destructions + 2);
scp = QSharedPointer<SomeClass>(sc);
QVERIFY(!scp.isNull());
QCOMPARE(scp.data(), sc);
QCOMPARE(Data::generationCounter, generations + 3);
QCOMPARE(Data::destructorCounter, destructions + 2);
QSharedPointer<SomeClass> scp2;
QVERIFY(scp2.isNull());
QCOMPARE(Data::generationCounter, generations + 3);
QCOMPARE(Data::destructorCounter, destructions + 2);
scp2 = sc->sharedFromThis();
QVERIFY(!scp2.isNull());
QVERIFY(scp == scp2);
QCOMPARE(scp2.data(), sc);
QCOMPARE(Data::generationCounter, generations + 3);
QCOMPARE(Data::destructorCounter, destructions + 2);
QSharedPointer<const SomeClass> scp3;
QVERIFY(scp3.isNull());
QCOMPARE(Data::generationCounter, generations + 3);
QCOMPARE(Data::destructorCounter, destructions + 2);
scp3 = sc->sharedFromThis();
QVERIFY(!scp3.isNull());
QVERIFY(scp == scp3);
QVERIFY(scp2 == scp3);
QCOMPARE(scp3.data(), sc);
QCOMPARE(Data::generationCounter, generations + 3);
QCOMPARE(Data::destructorCounter, destructions + 2);
QSharedPointer<SomeClass> scp4;
QVERIFY(scp4.isNull());
QCOMPARE(Data::generationCounter, generations + 3);
QCOMPARE(Data::destructorCounter, destructions + 2);
scp4 = sc->getSharedPtr();
QVERIFY(!scp4.isNull());
QVERIFY(scp == scp4);
QVERIFY(scp2 == scp4);
QVERIFY(scp3 == scp4);
QCOMPARE(scp4.data(), sc);
QCOMPARE(Data::generationCounter, generations + 3);
QCOMPARE(Data::destructorCounter, destructions + 2);
QSharedPointer<const SomeClass> scp5;
QVERIFY(scp5.isNull());
QCOMPARE(Data::generationCounter, generations + 3);
QCOMPARE(Data::destructorCounter, destructions + 2);
scp5 = const_cast<const SomeClass *>(sc)->getSharedPtr();
QVERIFY(!scp4.isNull());
QVERIFY(scp == scp5);
QVERIFY(scp2 == scp5);
QVERIFY(scp3 == scp5);
QVERIFY(scp4 == scp5);
QCOMPARE(scp5.data(), sc);
QCOMPARE(Data::generationCounter, generations + 3);
QCOMPARE(Data::destructorCounter, destructions + 2);
}
QCOMPARE(Data::generationCounter, generations + 3);
QCOMPARE(Data::destructorCounter, destructions + 3);
QSharedPointer<SomeClass> scp;
QVERIFY(scp.isNull());
{
QSharedPointer<SomeClass> scp2(new SomeClass());
QVERIFY(!scp2.isNull());
scp = scp2->sharedFromThis();
QVERIFY(!scp.isNull());
QVERIFY(scp == scp2);
QCOMPARE(Data::generationCounter, generations + 4);
QCOMPARE(Data::destructorCounter, destructions + 3);
}
QCOMPARE(Data::generationCounter, generations + 4);
QCOMPARE(Data::destructorCounter, destructions + 3);
QVERIFY(!scp.isNull());
{
QSharedPointer<const SomeClass> scp2;
scp2 = scp->sharedFromThis();
QVERIFY(!scp2.isNull());
QVERIFY(scp == scp2);
QCOMPARE(Data::generationCounter, generations + 4);
QCOMPARE(Data::destructorCounter, destructions + 3);
}
QCOMPARE(Data::generationCounter, generations + 4);
QCOMPARE(Data::destructorCounter, destructions + 3);
QVERIFY(!scp.isNull());
{
QSharedPointer<SomeClass> scp2;
scp2 = scp->getSharedPtr();
QVERIFY(!scp2.isNull());
QVERIFY(scp == scp2);
QCOMPARE(Data::generationCounter, generations + 4);
QCOMPARE(Data::destructorCounter, destructions + 3);
}
QCOMPARE(Data::generationCounter, generations + 4);
QCOMPARE(Data::destructorCounter, destructions + 3);
QVERIFY(!scp.isNull());
{
QSharedPointer<const SomeClass> scp2;
scp2 = qSharedPointerConstCast<const SomeClass>(scp)->getSharedPtr();
QVERIFY(!scp2.isNull());
QVERIFY(scp == scp2);
QCOMPARE(Data::generationCounter, generations + 4);
QCOMPARE(Data::destructorCounter, destructions + 3);
}
QCOMPARE(Data::generationCounter, generations + 4);
QCOMPARE(Data::destructorCounter, destructions + 3);
QVERIFY(!scp.isNull());
{
QSharedPointer<SomeClass> scp2;
scp2 = scp->sharedFromThis();
QVERIFY(!scp2.isNull());
QVERIFY(scp == scp2);
QCOMPARE(Data::generationCounter, generations + 4);
QCOMPARE(Data::destructorCounter, destructions + 3);
scp2.clear();
QCOMPARE(Data::generationCounter, generations + 4);
QCOMPARE(Data::destructorCounter, destructions + 3);
QVERIFY(!scp.isNull());
QVERIFY(scp2.isNull());
}
QCOMPARE(Data::generationCounter, generations + 4);
QCOMPARE(Data::destructorCounter, destructions + 3);
QVERIFY(!scp.isNull());
scp.clear();
QCOMPARE(Data::generationCounter, generations + 4);
QCOMPARE(Data::destructorCounter, destructions + 4);
{
QSharedPointer<SomeClass> scp2 = QSharedPointer<SomeClass>::create();
QVERIFY(!scp2.isNull());
scp = scp2->sharedFromThis();
QVERIFY(!scp.isNull());
QVERIFY(scp == scp2);
QCOMPARE(Data::generationCounter, generations + 5);
QCOMPARE(Data::destructorCounter, destructions + 4);
}
QCOMPARE(Data::generationCounter, generations + 5);
QCOMPARE(Data::destructorCounter, destructions + 4);
scp.clear();
QCOMPARE(Data::generationCounter, generations + 5);
QCOMPARE(Data::destructorCounter, destructions + 5);
{
QSharedPointer<const SomeClass> scp2(new SomeClass());
QVERIFY(!scp2.isNull());
QCOMPARE(Data::generationCounter, generations + 6);
QCOMPARE(Data::destructorCounter, destructions + 5);
QWeakPointer<const SomeClass> wcp2(scp2.constCast<SomeClass>());
QVERIFY(!wcp2.isNull());
QCOMPARE(Data::generationCounter, generations + 6);
QCOMPARE(Data::destructorCounter, destructions + 5);
}
QCOMPARE(Data::generationCounter, generations + 6);
QCOMPARE(Data::destructorCounter, destructions + 6);
}
#ifndef QT_NO_EXCEPTIONS
class ThrowData: public Data
{
public:
static int childDestructorCounter;
static int childGenerationCounter;
ThrowData()
{
childGenerationCounter++;
throw QStringLiteral("Dummy exception");
}
~ThrowData()
{
childDestructorCounter++;
}
};
int ThrowData::childDestructorCounter = 0;
int ThrowData::childGenerationCounter = 0;
#endif // !QT_NO_EXCEPTIONS
void tst_QSharedPointer::constructorThrow()
{
#ifndef QT_NO_EXCEPTIONS
int generation = Data::generationCounter;
int destructorCounter = Data::destructorCounter;
int childGeneration = ThrowData::childGenerationCounter;
int childDestructorCounter = ThrowData::childDestructorCounter;
QSharedPointer<ThrowData> ptr;
QVERIFY_THROWS_EXCEPTION(QString, ptr = QSharedPointer<ThrowData>::create());
QVERIFY(ptr.isNull());
QCOMPARE(ThrowData::childGenerationCounter, childGeneration + 1);
// destructor should never be called, if a constructor throws
// an exception
QCOMPARE(ThrowData::childDestructorCounter, childDestructorCounter);
QCOMPARE(Data::generationCounter, generation + 1);
// but base class constructor doesn't throw, so base class destructor
// should be called
QCOMPARE(Data::destructorCounter, destructorCounter + 1);
#else
QSKIP("Needs exceptions");
#endif // !QT_NO_EXCEPTIONS
}
namespace ReentrancyWhileDestructing {
struct IB
{
virtual ~IB() {}
};
struct IA
{
virtual QSharedPointer<IB> getB() = 0;
};
struct B: public IB
{
IA *m_a;
B(IA *a_a) :m_a(a_a)
{ }
~B()
{
QSharedPointer<IB> b = m_a->getB();
}
};
struct A: public IA
{
QSharedPointer<IB> b;
virtual QSharedPointer<IB> getB() override
{
return b;
}
A()
{
b = QSharedPointer<IB>(new B(this));
}
~A()
{
b.clear();
}
};
}
// This is a regression test for QTBUG-11730, where there would be a crash if
// the destructor of a QSharedPointer object being deleted recursed back into
// the same QSharedPointer object. There are no explicit verification steps
// in this test -- it is sufficient that the code does not crash.
void tst_QSharedPointer::reentrancyWhileDestructing()
{
ReentrancyWhileDestructing::A obj;
}
namespace {
struct Base1 {};
struct Base2 {};
struct Child1 : Base1 {};
struct Child2 : Base2 {};
template<template<typename> class SmartPtr>
struct Overloaded
{
std::array<int, 1> call(const SmartPtr<const Base1> &)
{
return {};
}
std::array<int, 2> call(const SmartPtr<const Base2> &)
{
return {};
}
static const constexpr uint base1Called = sizeof(std::array<int, 1>);
static const constexpr uint base2Called = sizeof(std::array<int, 2>);
void test()
{
#define QVERIFY_CALLS(expr, base) static_assert(sizeof(call(expr)) == base##Called)
QVERIFY_CALLS(SmartPtr<Base1>{}, base1);
QVERIFY_CALLS(SmartPtr<Base2>{}, base2);
QVERIFY_CALLS(SmartPtr<const Base1>{}, base1);
QVERIFY_CALLS(SmartPtr<const Base2>{}, base2);
QVERIFY_CALLS(SmartPtr<Child1>{}, base1);
QVERIFY_CALLS(SmartPtr<Child2>{}, base2);
QVERIFY_CALLS(SmartPtr<const Child1>{}, base1);
QVERIFY_CALLS(SmartPtr<const Child2>{}, base2);
#undef QVERIFY_CALLS
}
};
}
void tst_QSharedPointer::overloads()
{
Overloaded<QSharedPointer> sharedOverloaded;
sharedOverloaded.test();
Overloaded<QWeakPointer> weakOverloaded;
weakOverloaded.test();
}
void tst_QSharedPointer::ownerComparisons()
{
using SP = QSharedPointer<int>;
using WP = QWeakPointer<int>;
#define CHECK_EQ(a, b) \
do { \
QVERIFY(a.owner_equal(b)); \
QVERIFY(b.owner_equal(a)); \
QVERIFY(!a.owner_before(b)); \
QVERIFY(!b.owner_before(a)); \
QVERIFY(a.owner_hash() == b.owner_hash()); \
} while (false)
#define CHECK_NOT_EQ(a, b) \
do { \
QVERIFY(!a.owner_equal(b)); \
QVERIFY(!b.owner_equal(a)); \
QVERIFY(a.owner_before(b) || b.owner_before(a)); \
} while (false)
// null
{
SP sp1;
SP sp2;
WP wp1 = sp1;
WP wp2;
CHECK_EQ(sp1, sp1);
CHECK_EQ(sp1, sp2);
CHECK_EQ(sp1, wp1);
CHECK_EQ(sp2, wp2);
CHECK_EQ(wp1, wp1);
CHECK_EQ(wp1, wp2);
CHECK_EQ(wp2, wp2);
}
// same owner
{
SP sp1 = SP::create(123);
SP sp2 = sp1;
WP wp1 = sp1;
SP wp2 = sp2;
CHECK_EQ(sp1, sp1);
CHECK_EQ(sp1, sp2);
CHECK_EQ(sp1, wp1);
CHECK_EQ(sp2, wp2);
CHECK_EQ(wp1, wp1);
CHECK_EQ(wp1, wp2);
}
// owning vs null
{
SP sp1 = SP::create(123);
SP sp2;
WP wp1 = sp1;
WP wp2 = sp2;
CHECK_EQ(sp1, sp1);
CHECK_NOT_EQ(sp1, sp2);
CHECK_EQ(sp1, wp1);
CHECK_EQ(sp2, wp2);
CHECK_EQ(wp1, wp1);
CHECK_NOT_EQ(wp1, wp2);
}
// different owners
{
SP sp1 = SP::create(123);
SP sp2 = SP::create(456);
WP wp1 = sp1;
WP wp2 = sp2;
CHECK_EQ(sp1, sp1);
CHECK_NOT_EQ(sp1, sp2);
CHECK_EQ(sp1, wp1);
CHECK_EQ(sp2, wp2);
CHECK_EQ(wp1, wp1);
CHECK_NOT_EQ(wp1, wp2);
}
// reset vs. null
{
SP sp1 = SP::create(123);
SP sp2;
WP wp1 = sp1;
WP wp2;
CHECK_EQ(sp1, sp1);
CHECK_NOT_EQ(sp1, sp2);
CHECK_EQ(sp1, wp1);
CHECK_NOT_EQ(sp1, wp2);
CHECK_EQ(wp1, wp1);
CHECK_NOT_EQ(wp1, wp2);
sp1.reset();
CHECK_EQ(sp1, sp1);
CHECK_EQ(sp1, sp2);
CHECK_NOT_EQ(sp1, wp1);
CHECK_EQ(sp2, wp2);
CHECK_EQ(wp1, wp1);
CHECK_NOT_EQ(wp1, wp2);
}
// expired weak pointers
{
WP wp1 = SP::create(123);
WP wp2;
CHECK_EQ(wp1, wp1);
CHECK_NOT_EQ(wp1, wp2);
}
{
WP wp1 = SP::create(123);
WP wp2 = wp1;
CHECK_EQ(wp1, wp1);
CHECK_EQ(wp1, wp2);
}
{
WP wp1 = SP::create(123);
WP wp2 = SP::create(456);
CHECK_EQ(wp1, wp1);
CHECK_EQ(wp2, wp2);
CHECK_NOT_EQ(wp1, wp2);
}
#undef CHECK_EQ
#undef CHECK_NOT_EQ
}
QTEST_MAIN(tst_QSharedPointer)
#include "tst_qsharedpointer.moc"