/**************************************************************************** ** ** Copyright (C) 2016 The Qt Company Ltd. ** Copyright (C) 2016 Intel Corporation. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the test suite of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #define QT_SHAREDPOINTER_TRACK_POINTERS #include "qsharedpointer.h" #include #include #include #include #include #include "externaltests.h" #include "forwarddeclared.h" #include "nontracked.h" #include "wrapper.h" #include #include #include #ifdef Q_OS_UNIX #include #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 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(); #if QT_DEPRECATED_SINCE(5, 0) void noSharedPointerFromWeakQObject(); void sharedPointerFromQObjectWithWeak(); #endif void weakQObjectFromSharedPointer(); void objectCast(); void objectCastStdSharedPtr(); void differentPointers(); void virtualBaseDifferentPointers(); #ifndef QTEST_NO_RTTI void dynamicCast(); void dynamicCastDifferentPointers(); void dynamicCastVirtualBase(); void dynamicCastFailure(); void dynamicCastFailureNoLeak(); #endif void constCorrectness(); void customDeleter(); void lambdaCustomDeleter(); void creating(); void creatingCvQualified(); void creatingVariadic(); void creatingQObject(); void mixTrackingPointerCode(); void reentrancyWhileDestructing(); void map(); void hash(); void qvariantCast(); void sharedFromThis(); void constructorThrow(); void threadStressTest_data(); void threadStressTest(); void validConstructs(); void invalidConstructs_data(); void invalidConstructs(); // 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) // 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 static inline QtSharedPointer::ExternalRefCountData *refCountData(const QSharedPointer &b) { // access d-pointer: struct Dummy { void* value; QtSharedPointer::ExternalRefCountData* data; }; // sanity checks: Q_STATIC_ASSERT(sizeof(QSharedPointer) == sizeof(Dummy)); Q_ASSERT(static_cast(static_cast(&b))->value == b.data()); return static_cast(static_cast(&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 i; NoDefaultConstructorRRef2(std::unique_ptr &&i) : i(std::move(i)) {} }; void tst_QSharedPointer::basics_data() { QTest::addColumn("isNull"); QTest::newRow("null") << true; QTest::newRow("non-null") << false; } void tst_QSharedPointer::basics() { { QSharedPointer ptr; QWeakPointer weakref; QCOMPARE(sizeof(ptr), 2*sizeof(void*)); QCOMPARE(sizeof(weakref), 2*sizeof(void*)); } { QSharedPointer ptr; QWeakPointer 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 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); } QVERIFY(ptr == aData); QVERIFY(!(ptr != aData)); QVERIFY(aData == ptr); QVERIFY(!(aData != ptr)); QVERIFY(ptr != otherData); QVERIFY(otherData != ptr); QVERIFY(! (ptr == otherData)); QVERIFY(! (otherData == ptr)); } QVERIFY(!refCountData(ptr) || refCountData(ptr)->weakref.loadRelaxed() == 1); QVERIFY(!refCountData(ptr) || refCountData(ptr)->strongref.loadRelaxed() == 1); { // create another object: QSharedPointer otherCopy(otherData); QVERIFY(ptr != otherCopy); QVERIFY(otherCopy != ptr); QVERIFY(! (ptr == otherCopy)); QVERIFY(! (otherCopy == ptr)); // otherData is deleted here } QVERIFY(!refCountData(ptr) || refCountData(ptr)->weakref.loadRelaxed() == 1); QVERIFY(!refCountData(ptr) || refCountData(ptr)->strongref.loadRelaxed() == 1); { // create a copy: QSharedPointer copy(ptr); QVERIFY(copy == ptr); QVERIFY(ptr == copy); QVERIFY(! (copy != ptr)); QVERIFY(! (ptr != copy)); 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 weak(ptr); QCOMPARE(weak.isNull(), isNull); QCOMPARE(!weak, isNull); QCOMPARE(bool(weak), !isNull); QVERIFY(ptr == weak); QVERIFY(weak == ptr); QVERIFY(! (ptr != weak)); QVERIFY(! (weak != ptr)); // create another reference: QWeakPointer 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 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 p1; QSharedPointer 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); QVERIFY(p1 < p2); QVERIFY(!(p2 < p1)); QVERIFY(!(p2 < p2)); QVERIFY(!(p1 < p1)); // 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 p1(nullptr); QSharedPointer p2 = nullptr; QSharedPointer null; QVERIFY(p1 == null); QVERIFY(p1 == nullptr); QVERIFY(nullptr == p1); QVERIFY(!p1); QVERIFY(!p1.data()); QVERIFY(!p1.get()); QVERIFY(p2 == null); QVERIFY(p2 == nullptr); QVERIFY(nullptr == p2); QVERIFY(!p2); QVERIFY(!p2.data()); QVERIFY(!p2.get()); QVERIFY(p1 == p2); QSharedPointer p3 = p1; QVERIFY(p3 == p1); QVERIFY(p3 == null); QVERIFY(p3 == nullptr); QVERIFY(nullptr == p3); QVERIFY(!p3.data()); QVERIFY(!p3.get()); p3 = nullptr; // check for non-ambiguity QSharedPointer p1_zero(0); QSharedPointer p2_zero = 0; p3 = 0; QSharedPointer p4(new char); QVERIFY(p4); QVERIFY(p4.data()); QVERIFY(p4.get()); QVERIFY(p4 != nullptr); QVERIFY(nullptr != p4); QVERIFY(p4 != p1); QVERIFY(p4 != p2); QVERIFY(p4 != null); QVERIFY(p4 != p3); } void tst_QSharedPointer::swap() { QSharedPointer 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 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 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 p3 = std::move(p1); QVERIFY(p1 != control); QVERIFY(p1.isNull()); QVERIFY(p3 == control); QVERIFY(!p3.isNull()); QVERIFY(*p3 == 42); QWeakPointer 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 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 sp; // copying should work, too: QSharedPointer sp2 = sp; // and assignment: QSharedPointer sp3; sp3 = sp; // move assignment: QSharedPointer sp4; sp4 = std::move(sp); // and move constuction: QSharedPointer sp5 = std::move(sp2); // swapping: sp4.swap(sp3); qSwap(sp4, sp3); // and, of course, destruction } void tst_QSharedPointer::memoryManagement() { int generation = Data::generationCounter + 1; int destructorCounter = Data::destructorCounter; QSharedPointer ptr = QSharedPointer(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 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 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 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 weak = ptr; ptr = QSharedPointer(); // 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); } 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 ptr(new NVDData); } QCOMPARE(NVDData::destructorCounter, 1); QCOMPARE(NVDDerivedData::destructorCounter, 0); NVDData::destructorCounter = NVDDerivedData::destructorCounter = 0; { QSharedPointer ptr(new NVDDerivedData); } QCOMPARE(NVDData::destructorCounter, 1); QCOMPARE(NVDDerivedData::destructorCounter, 1); NVDData::destructorCounter = NVDDerivedData::destructorCounter = 0; { QSharedPointer bptr; QSharedPointer ptr(new NVDDerivedData); bptr = ptr; } QCOMPARE(NVDData::destructorCounter, 1); QCOMPARE(NVDDerivedData::destructorCounter, 1); NVDData::destructorCounter = NVDDerivedData::destructorCounter = 0; { QSharedPointer ptr(new NVDDerivedData); } QCOMPARE(NVDData::destructorCounter, 1); QCOMPARE(NVDDerivedData::destructorCounter, 1); } void tst_QSharedPointer::lock() { QSharedPointer sp = QSharedPointer::create(); QVERIFY(sp); QWeakPointer 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; } virtual void virtualDelete() { delete this; } virtual int classLevel() { 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: virtual int classLevel() { return 3; } }; class VirtualDerived: virtual public Data { public: int moreData; VirtualDerived() : moreData(0xc0ffee) { } virtual int classLevel() { return 4; } }; void tst_QSharedPointer::downCast() { { QSharedPointer ptr = QSharedPointer(new DerivedData); QSharedPointer baseptr = qSharedPointerCast(ptr); QSharedPointer other; QVERIFY(ptr == baseptr); QVERIFY(baseptr == ptr); QVERIFY(! (ptr != baseptr)); QVERIFY(! (baseptr != ptr)); QVERIFY(ptr != other); QVERIFY(other != ptr); QVERIFY(! (ptr == other)); QVERIFY(! (other == ptr)); } { QSharedPointer ptr = QSharedPointer(new DerivedData); QSharedPointer baseptr = ptr; } int destructorCount; destructorCount = DerivedData::derivedDestructorCounter; { QSharedPointer baseptr; { QSharedPointer ptr = QSharedPointer(new DerivedData); baseptr = ptr; QVERIFY(baseptr == ptr); } } QCOMPARE(DerivedData::derivedDestructorCounter, destructorCount + 1); destructorCount = DerivedData::derivedDestructorCounter; { QSharedPointer ptr = QSharedPointer(new DerivedData); QWeakPointer baseptr = ptr; QVERIFY(baseptr == ptr); ptr = QSharedPointer(); QVERIFY(baseptr.isNull()); } QCOMPARE(DerivedData::derivedDestructorCounter, destructorCount + 1); destructorCount = DerivedData::derivedDestructorCounter; { QSharedPointer ptr = QSharedPointer(new DerivedData); QWeakPointer weakptr(ptr); QSharedPointer baseptr = weakptr; QVERIFY(baseptr == ptr); QWeakPointer baseweakptr = weakptr; QVERIFY(baseweakptr == ptr); } QCOMPARE(DerivedData::derivedDestructorCounter, destructorCount + 1); } void functionDataByValue(QSharedPointer p) { Q_UNUSED(p); }; void functionDataByRef(const QSharedPointer &p) { Q_UNUSED(p); }; void tst_QSharedPointer::functionCallDownCast() { QSharedPointer p(new DerivedData()); functionDataByValue(p); functionDataByRef(p); } void tst_QSharedPointer::upCast() { QSharedPointer baseptr = QSharedPointer(new DerivedData); { QSharedPointer derivedptr = qSharedPointerCast(baseptr); QVERIFY(baseptr == derivedptr); QCOMPARE(static_cast(derivedptr.data()), baseptr.data()); } QCOMPARE(int(refCountData(baseptr)->weakref.loadRelaxed()), 1); QCOMPARE(int(refCountData(baseptr)->strongref.loadRelaxed()), 1); { QWeakPointer derivedptr = qWeakPointerCast(baseptr); QVERIFY(baseptr == derivedptr); } QCOMPARE(int(refCountData(baseptr)->weakref.loadRelaxed()), 1); QCOMPARE(int(refCountData(baseptr)->strongref.loadRelaxed()), 1); { QWeakPointer weakptr = baseptr; QSharedPointer derivedptr = qSharedPointerCast(weakptr); QVERIFY(baseptr == derivedptr); QCOMPARE(static_cast(derivedptr.data()), baseptr.data()); } QCOMPARE(int(refCountData(baseptr)->weakref.loadRelaxed()), 1); QCOMPARE(int(refCountData(baseptr)->strongref.loadRelaxed()), 1); { QSharedPointer derivedptr = baseptr.staticCast(); QVERIFY(baseptr == derivedptr); QCOMPARE(static_cast(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 weak; weak = QWeakPointer(); QVERIFY(weak.isNull()); QVERIFY(weak.toStrongRef().isNull()); } { QObject *obj = new QObject; QSharedPointer shared(obj); QWeakPointer 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 shared(obj); QWeakPointer 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 shared(obj); QWeakPointer weak(shared); { QWeakPointer 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 shared(obj); QWeakPointer weak(shared); { QWeakPointer weak2(shared); QVERIFY(!weak2.isNull()); shared.reset(); QVERIFY(weak.isNull()); QVERIFY(weak2.isNull()); } QVERIFY(weak.isNull()); } safetyCheck(); #if QT_DEPRECATED_SINCE(5, 0) { QWeakPointer weak; weak = QWeakPointer(); QVERIFY(weak.isNull()); QVERIFY(!weak.data()); } { QObject *obj = new QObject; QWeakPointer weak(obj); QVERIFY(!weak.isNull()); QVERIFY(weak.data() == obj); // now delete delete obj; QVERIFY(weak.isNull()); } safetyCheck(); { // same, bit with operator= QObject *obj = new QObject; QWeakPointer weak; weak = obj; QVERIFY(!weak.isNull()); QVERIFY(weak.data() == obj); // now delete delete obj; QVERIFY(weak.isNull()); } safetyCheck(); { // delete triggered by parent QObject *obj, *parent; parent = new QObject; obj = new QObject(parent); QWeakPointer weak(obj); // now delete the parent delete parent; QVERIFY(weak.isNull()); } safetyCheck(); { // same as above, but set the parent after QWeakPointer is created QObject *obj, *parent; obj = new QObject; QWeakPointer weak(obj); parent = new QObject; obj->setParent(parent); // now delete the parent delete parent; QVERIFY(weak.isNull()); } safetyCheck(); { // with two QWeakPointers QObject *obj = new QObject; QWeakPointer weak(obj); { QWeakPointer weak2(obj); QVERIFY(!weak2.isNull()); QVERIFY(weak == weak2); } QVERIFY(!weak.isNull()); delete obj; QVERIFY(weak.isNull()); } safetyCheck(); { // same, but delete the pointer while two QWeakPointers exist QObject *obj = new QObject; QWeakPointer weak(obj); { QWeakPointer weak2(obj); QVERIFY(!weak2.isNull()); delete obj; QVERIFY(weak.isNull()); QVERIFY(weak2.isNull()); } QVERIFY(weak.isNull()); } safetyCheck(); #endif } #if QT_DEPRECATED_SINCE(5, 0) void tst_QSharedPointer::noSharedPointerFromWeakQObject() { // you're not allowed to create a QSharedPointer from an unmanaged QObject QObject obj; QWeakPointer weak(&obj); { QTest::ignoreMessage(QtWarningMsg , "QSharedPointer: cannot create a QSharedPointer from a QObject-tracking QWeakPointer"); QSharedPointer strong = weak.toStrongRef(); QVERIFY(strong.isNull()); } { QTest::ignoreMessage(QtWarningMsg , "QSharedPointer: cannot create a QSharedPointer from a QObject-tracking QWeakPointer"); QSharedPointer strong = weak; QVERIFY(strong.isNull()); } QCOMPARE(weak.data(), &obj); // if something went wrong, we'll probably crash here } void tst_QSharedPointer::sharedPointerFromQObjectWithWeak() { QObject *ptr = new QObject; QWeakPointer weak = ptr; { QSharedPointer shared(ptr); QVERIFY(!weak.isNull()); QCOMPARE(shared.data(), ptr); QCOMPARE(weak.data(), ptr); } QVERIFY(weak.isNull()); } #endif void tst_QSharedPointer::weakQObjectFromSharedPointer() { #if QT_DEPRECATED_SINCE(5, 0) { // this is the inverse of the above: you're allowed to create a QWeakPointer // from a managed QObject QSharedPointer shared(new QObject); QWeakPointer weak = shared.data(); QVERIFY(!weak.isNull()); // delete: shared.clear(); QVERIFY(weak.isNull()); } #endif { QSharedPointer shared(new QObject); QWeakPointer weak = shared; QVERIFY(!weak.isNull()); // delete: shared.clear(); QVERIFY(weak.isNull()); } } void tst_QSharedPointer::objectCast() { { OtherObject *data = new OtherObject; QSharedPointer baseptr = QSharedPointer(data); QVERIFY(baseptr == data); QVERIFY(data == baseptr); // perform object cast QSharedPointer ptr = qSharedPointerObjectCast(baseptr); QVERIFY(!ptr.isNull()); QCOMPARE(ptr.data(), data); QVERIFY(ptr == data); // again: ptr = baseptr.objectCast(); QVERIFY(ptr == data); // again: ptr = qobject_cast(baseptr); QVERIFY(ptr == data); // again: ptr = qobject_cast >(baseptr); QVERIFY(ptr == data); } safetyCheck(); { const OtherObject *data = new OtherObject; QSharedPointer baseptr = QSharedPointer(data); QVERIFY(baseptr == data); QVERIFY(data == baseptr); // perform object cast QSharedPointer ptr = qSharedPointerObjectCast(baseptr); QVERIFY(!ptr.isNull()); QCOMPARE(ptr.data(), data); QVERIFY(ptr == data); // again: ptr = baseptr.objectCast(); QVERIFY(ptr == data); // again: ptr = qobject_cast(baseptr); QVERIFY(ptr == data); // again: ptr = qobject_cast >(baseptr); QVERIFY(ptr == data); } safetyCheck(); { OtherObject *data = new OtherObject; QSharedPointer ptr = QSharedPointer(data); QWeakPointer weakptr = ptr; { // perform object cast QSharedPointer otherptr = qSharedPointerObjectCast(weakptr); QVERIFY(otherptr == ptr); // again: otherptr = qobject_cast(weakptr); QVERIFY(otherptr == ptr); // again: otherptr = qobject_cast >(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 otherptr = qSharedPointerObjectCast(weakptr); QVERIFY(otherptr.isNull()); // again: otherptr = qobject_cast(weakptr); QVERIFY(otherptr.isNull()); // again: otherptr = qobject_cast >(weakptr); QVERIFY(otherptr.isNull()); } safetyCheck(); } void tst_QSharedPointer::objectCastStdSharedPtr() { { OtherObject *data = new OtherObject; std::shared_ptr baseptr = std::shared_ptr(data); QVERIFY(baseptr.get() == data); // perform successful object cast std::shared_ptr ptr = qobject_pointer_cast(baseptr); QVERIFY(ptr.get()); QVERIFY(ptr.get() == data); QVERIFY(baseptr.get() == data); } { OtherObject *data = new OtherObject; std::shared_ptr baseptr = std::shared_ptr(data); QVERIFY(baseptr.get() == data); // perform successful object cast std::shared_ptr ptr = qobject_pointer_cast(std::move(baseptr)); QVERIFY(ptr.get()); QVERIFY(ptr.get() == data); QVERIFY(!baseptr.get()); } { QObject *data = new QObject; std::shared_ptr baseptr = std::shared_ptr(data); QVERIFY(baseptr.get() == data); // perform unsuccessful object cast std::shared_ptr ptr = qobject_pointer_cast(baseptr); QVERIFY(!ptr.get()); QVERIFY(baseptr.get() == data); } { QObject *data = new QObject; std::shared_ptr baseptr = std::shared_ptr(data); QVERIFY(baseptr.get() == data); // perform unsuccessful object cast std::shared_ptr ptr = qobject_pointer_cast(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(&aData) == *reinterpret_cast(&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 ptr = QSharedPointer(aData); QSharedPointer baseptr = qSharedPointerCast(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(&aData) != *reinterpret_cast(&aBase)); QSharedPointer baseptr = QSharedPointer(aData); QSharedPointer ptr = qSharedPointerCast(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(&aData) != *reinterpret_cast(&aBase)); QSharedPointer ptr = QSharedPointer(aData); QSharedPointer 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(&aData) != *reinterpret_cast(&aBase)); QSharedPointer ptr = QSharedPointer(aData); QSharedPointer baseptr = qSharedPointerCast(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(&aData) != *reinterpret_cast(&aBase)); QSharedPointer ptr = QSharedPointer(aData); QSharedPointer baseptr = ptr; QVERIFY(ptr == baseptr); QVERIFY(ptr.data() == baseptr.data()); QVERIFY(ptr == aBase); QVERIFY(ptr == aData); QVERIFY(baseptr == aData); QVERIFY(baseptr == aBase); } safetyCheck(); } #ifndef QTEST_NO_RTTI void tst_QSharedPointer::dynamicCast() { DerivedData *aData = new DerivedData; QSharedPointer baseptr = QSharedPointer(aData); { QSharedPointer derivedptr = qSharedPointerDynamicCast(baseptr); QVERIFY(baseptr == derivedptr); QCOMPARE(derivedptr.data(), aData); QCOMPARE(static_cast(derivedptr.data()), baseptr.data()); } QCOMPARE(int(refCountData(baseptr)->weakref.loadRelaxed()), 1); QCOMPARE(int(refCountData(baseptr)->strongref.loadRelaxed()), 1); { QWeakPointer weakptr = baseptr; QSharedPointer derivedptr = qSharedPointerDynamicCast(weakptr); QVERIFY(baseptr == derivedptr); QCOMPARE(derivedptr.data(), aData); QCOMPARE(static_cast(derivedptr.data()), baseptr.data()); } QCOMPARE(int(refCountData(baseptr)->weakref.loadRelaxed()), 1); QCOMPARE(int(refCountData(baseptr)->strongref.loadRelaxed()), 1); { QSharedPointer derivedptr = baseptr.dynamicCast(); QVERIFY(baseptr == derivedptr); QCOMPARE(derivedptr.data(), aData); QCOMPARE(static_cast(derivedptr.data()), baseptr.data()); } QCOMPARE(int(refCountData(baseptr)->weakref.loadRelaxed()), 1); QCOMPARE(int(refCountData(baseptr)->strongref.loadRelaxed()), 1); } void tst_QSharedPointer::dynamicCastDifferentPointers() { // DiffPtrDerivedData derives from both Data and Stuffing DiffPtrDerivedData *aData = new DiffPtrDerivedData; QSharedPointer baseptr = QSharedPointer(aData); { QSharedPointer derivedptr = qSharedPointerDynamicCast(baseptr); QVERIFY(baseptr == derivedptr); QCOMPARE(derivedptr.data(), aData); QCOMPARE(static_cast(derivedptr.data()), baseptr.data()); } QCOMPARE(int(refCountData(baseptr)->weakref.loadRelaxed()), 1); QCOMPARE(int(refCountData(baseptr)->strongref.loadRelaxed()), 1); { QWeakPointer weakptr = baseptr; QSharedPointer derivedptr = qSharedPointerDynamicCast(weakptr); QVERIFY(baseptr == derivedptr); QCOMPARE(derivedptr.data(), aData); QCOMPARE(static_cast(derivedptr.data()), baseptr.data()); } QCOMPARE(int(refCountData(baseptr)->weakref.loadRelaxed()), 1); QCOMPARE(int(refCountData(baseptr)->strongref.loadRelaxed()), 1); { QSharedPointer derivedptr = baseptr.dynamicCast(); QVERIFY(baseptr == derivedptr); QCOMPARE(derivedptr.data(), aData); QCOMPARE(static_cast(derivedptr.data()), baseptr.data()); } QCOMPARE(int(refCountData(baseptr)->weakref.loadRelaxed()), 1); QCOMPARE(int(refCountData(baseptr)->strongref.loadRelaxed()), 1); { Stuffing *nakedptr = dynamic_cast(baseptr.data()); QVERIFY(nakedptr); QSharedPointer otherbaseptr = qSharedPointerDynamicCast(baseptr); QVERIFY(!otherbaseptr.isNull()); QVERIFY(otherbaseptr == nakedptr); QCOMPARE(otherbaseptr.data(), nakedptr); QCOMPARE(static_cast(otherbaseptr.data()), aData); } } void tst_QSharedPointer::dynamicCastVirtualBase() { VirtualDerived *aData = new VirtualDerived; QSharedPointer baseptr = QSharedPointer(aData); { QSharedPointer derivedptr = qSharedPointerDynamicCast(baseptr); QVERIFY(baseptr == derivedptr); QCOMPARE(derivedptr.data(), aData); QCOMPARE(static_cast(derivedptr.data()), baseptr.data()); } QCOMPARE(int(refCountData(baseptr)->weakref.loadRelaxed()), 1); QCOMPARE(int(refCountData(baseptr)->strongref.loadRelaxed()), 1); { QWeakPointer weakptr = baseptr; QSharedPointer derivedptr = qSharedPointerDynamicCast(weakptr); QVERIFY(baseptr == derivedptr); QCOMPARE(derivedptr.data(), aData); QCOMPARE(static_cast(derivedptr.data()), baseptr.data()); } QCOMPARE(int(refCountData(baseptr)->weakref.loadRelaxed()), 1); QCOMPARE(int(refCountData(baseptr)->strongref.loadRelaxed()), 1); { QSharedPointer derivedptr = baseptr.dynamicCast(); QVERIFY(baseptr == derivedptr); QCOMPARE(derivedptr.data(), aData); QCOMPARE(static_cast(derivedptr.data()), baseptr.data()); } QCOMPARE(int(refCountData(baseptr)->weakref.loadRelaxed()), 1); QCOMPARE(int(refCountData(baseptr)->strongref.loadRelaxed()), 1); } void tst_QSharedPointer::dynamicCastFailure() { QSharedPointer baseptr = QSharedPointer(new Data); QVERIFY(dynamic_cast(baseptr.data()) == 0); { QSharedPointer derivedptr = qSharedPointerDynamicCast(baseptr); QVERIFY(derivedptr.isNull()); } QCOMPARE(int(refCountData(baseptr)->weakref.loadRelaxed()), 1); QCOMPARE(int(refCountData(baseptr)->strongref.loadRelaxed()), 1); { QSharedPointer derivedptr = baseptr.dynamicCast(); 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 ptr = QSharedPointer(new Data); QSharedPointer cptr(ptr); QSharedPointer vptr(ptr); cptr = ptr; vptr = ptr; ptr = qSharedPointerConstCast(cptr); ptr = qSharedPointerConstCast(vptr); ptr = cptr.constCast(); ptr = vptr.constCast(); QSharedPointer cvptr(ptr); QSharedPointer cvptr2(cptr); QSharedPointer cvptr3(vptr); cvptr = ptr; cvptr2 = cptr; cvptr3 = vptr; ptr = qSharedPointerConstCast(cvptr); ptr = cvptr.constCast(); } safetyCheck(); { Data *aData = new Data; QSharedPointer ptr = QSharedPointer(aData); const QSharedPointer cptr = ptr; ptr = cptr; QSharedPointer other = qSharedPointerCast(cptr); #ifndef QT_NO_DYNAMIC_CAST other = qSharedPointerDynamicCast(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 struct CustomDeleter { CustomDeleter() { ++refcount; } CustomDeleter(const CustomDeleter &) { ++refcount; } ~CustomDeleter() { --refcount; } inline void operator()(T *ptr) { delete ptr; ++callCount; } static int callCount; }; template int CustomDeleter::callCount = 0; void tst_QSharedPointer::customDeleter() { { QSharedPointer ptr(0, &Data::doDelete); QSharedPointer ptr2(0, &Data::alsoDelete); QSharedPointer ptr3(0, &Data::virtualDelete); } safetyCheck(); { QSharedPointer ptr(nullptr, &Data::doDelete); QSharedPointer ptr2(nullptr, &Data::alsoDelete); QSharedPointer ptr3(nullptr, &Data::virtualDelete); } safetyCheck(); { QSharedPointer ptr(new Data, &Data::doDelete); QSharedPointer ptr2(new Data, &Data::alsoDelete); QSharedPointer ptr3(new Data, &Data::virtualDelete); } safetyCheck(); { QSharedPointer ptr(new DerivedData, &Data::doDelete); QSharedPointer ptr2(new DerivedData, &Data::alsoDelete); QSharedPointer ptr3(new DerivedData, &Data::virtualDelete); } safetyCheck(); customDeleterFnCallCount = 0; { QSharedPointer ptr = QSharedPointer(new Data, customDeleterFn); ptr.data(); QCOMPARE(customDeleterFnCallCount, 0); } QCOMPARE(customDeleterFnCallCount, 1); safetyCheck(); customDeleterFnCallCount = 0; { QSharedPointer ptr = QSharedPointer(new Data, customDeleterFn); QCOMPARE(customDeleterFnCallCount, 0); ptr.clear(); QCOMPARE(customDeleterFnCallCount, 1); } QCOMPARE(customDeleterFnCallCount, 1); safetyCheck(); customDeleterFnCallCount = 0; { QSharedPointer ptr = QSharedPointer(new Data, customDeleterFn); QCOMPARE(customDeleterFnCallCount, 0); ptr = QSharedPointer(new Data); QCOMPARE(customDeleterFnCallCount, 1); } QCOMPARE(customDeleterFnCallCount, 1); safetyCheck(); customDeleterFnCallCount = 0; { QSharedPointer ptr = QSharedPointer(new DerivedData, customDeleterFn); ptr.data(); QCOMPARE(customDeleterFnCallCount, 0); } QCOMPARE(customDeleterFnCallCount, 1); safetyCheck(); customDeleterFnCallCount = 0; { QSharedPointer ptr = QSharedPointer(new DerivedData, customDeleterFn); ptr.data(); QCOMPARE(customDeleterFnCallCount, 0); } QCOMPARE(customDeleterFnCallCount, 1); safetyCheck(); customDeleterFnCallCount = 0; { QSharedPointer other; { QSharedPointer ptr = QSharedPointer(new Data, customDeleterFn); other = ptr; QCOMPARE(customDeleterFnCallCount, 0); } QCOMPARE(customDeleterFnCallCount, 0); } QCOMPARE(customDeleterFnCallCount, 1); safetyCheck(); customDeleterFnCallCount = 0; { QSharedPointer other; { QSharedPointer ptr = QSharedPointer(new DerivedData, customDeleterFn); other = ptr; QCOMPARE(customDeleterFnCallCount, 0); } QCOMPARE(customDeleterFnCallCount, 0); } QCOMPARE(customDeleterFnCallCount, 1); safetyCheck(); refcount = 0; CustomDeleter dataDeleter; dataDeleter.callCount = 0; { QSharedPointer ptr = QSharedPointer(new Data, dataDeleter); ptr.data(); QCOMPARE(dataDeleter.callCount, 0); } QCOMPARE(dataDeleter.callCount, 1); QCOMPARE(refcount, 1); safetyCheck(); dataDeleter.callCount = 0; { QSharedPointer ptr = QSharedPointer(new Data, dataDeleter); QSharedPointer other = ptr; other.clear(); QCOMPARE(dataDeleter.callCount, 0); } QCOMPARE(dataDeleter.callCount, 1); QCOMPARE(refcount, 1); safetyCheck(); dataDeleter.callCount = 0; { QSharedPointer other; { QSharedPointer ptr = QSharedPointer(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 ptr = QSharedPointer(new DerivedData, dataDeleter); ptr.data(); QCOMPARE(dataDeleter.callCount, 0); } QCOMPARE(dataDeleter.callCount, 1); QCOMPARE(refcount, 1); safetyCheck(); CustomDeleter derivedDataDeleter; derivedDataDeleter.callCount = 0; dataDeleter.callCount = 0; { QSharedPointer ptr = QSharedPointer(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 other; { QSharedPointer ptr = QSharedPointer(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 other; { QSharedPointer ptr = QSharedPointer(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 nvdeleter; nvdeleter.callCount = 0; { QSharedPointer ptr(new NVDData, nvdeleter); } QCOMPARE(nvdeleter.callCount, 1); safetyCheck(); CustomDeleter nvderiveddeleter; nvdeleter.callCount = 0; nvderiveddeleter.callCount = 0; { QSharedPointer ptr(new NVDDerivedData, nvderiveddeleter); } QCOMPARE(nvdeleter.callCount, 0); QCOMPARE(nvderiveddeleter.callCount, 1); safetyCheck(); nvdeleter.callCount = 0; nvderiveddeleter.callCount = 0; { QSharedPointer ptr(new NVDDerivedData, nvderiveddeleter); } QCOMPARE(nvdeleter.callCount, 0); QCOMPARE(nvderiveddeleter.callCount, 1); safetyCheck(); // a custom deleter with a different pointer parameter { QSharedPointer ptr(static_cast(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 ptr(new Data, [](Data *d) { delete d; }); QSharedPointer ptr2(new Data, [](Data *d) { d->doDelete(); }); } safetyCheck(); customDeleterFnCallCount = 0; { // stateless, one-argument, modifies globals QSharedPointer ptr(new Data, [](Data *d) { ++customDeleterFnCallCount; delete d; }); } safetyCheck(); QCOMPARE(customDeleterFnCallCount, 1); { // stateful by ref, one-argument int i = 0; QSharedPointer ptr(new Data, [&i](Data *d) { i = 42; delete d; }); ptr.clear(); QCOMPARE(i, 42); } safetyCheck(); } void customQObjectDeleterFn(QObject *obj) { ++customDeleterFnCallCount; delete obj; } void tst_QSharedPointer::creating() { Data::generationCounter = Data::destructorCounter = 0; { QSharedPointer ptr = QSharedPointer::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 ptr = QSharedPointer::create(); QWeakPointer 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 ptr = QSharedPointer::create(); QCOMPARE(ptr->classLevel(), 2); QCOMPARE(ptr.staticCast()->moreData, 0); ptr.clear(); QCOMPARE(Data::destructorCounter, 1); QCOMPARE(DerivedData::derivedDestructorCounter, 1); } safetyCheck(); { QSharedPointer ptr = QSharedPointer::create(); QCOMPARE(ptr->classLevel(), 3); QCOMPARE(ptr.staticCast()->buffer[7]+0, 16-7); QCOMPARE(ptr.staticCast()->buffer[3]+0, 16-3); QCOMPARE(ptr.staticCast()->buffer[0]+0, 16); } safetyCheck(); { QSharedPointer ptr = QSharedPointer::create(); QCOMPARE(ptr->classLevel(), 4); QCOMPARE(ptr->moreData, 0xc0ffee); QSharedPointer baseptr = ptr; QCOMPARE(baseptr->classLevel(), 4); } safetyCheck(); } void tst_QSharedPointer::creatingCvQualified() { auto cptr = QSharedPointer::create(); auto vptr = QSharedPointer::create(); auto cvptr = QSharedPointer::create(); } void tst_QSharedPointer::creatingVariadic() { int i = 42; { NoDefaultConstructor1(1); // control check QSharedPointer ptr = QSharedPointer::create(1); QCOMPARE(ptr->i, 1); NoDefaultConstructor1(0u); // control check ptr = QSharedPointer::create(0u); QCOMPARE(ptr->i, 42); NoDefaultConstructor1 x(i); // control check ptr = QSharedPointer::create(i); QCOMPARE(ptr->i, i); } { NoDefaultConstructor2((void*)0, 1); // control check QSharedPointer ptr = QSharedPointer::create((void*)0, 1); QCOMPARE(ptr->i, 1); QCOMPARE(ptr->ptr, (void*)0); int *null = 0; NoDefaultConstructor2(null, 2); // control check ptr = QSharedPointer::create(null, 2); QCOMPARE(ptr->i, 2); QCOMPARE(ptr->ptr, (void*)0); NoDefaultConstructor2(nullptr, 3); // control check ptr = QSharedPointer::create(nullptr, 3); QCOMPARE(ptr->i, 3); QCOMPARE(ptr->ptr, (void*)nullptr); } { NoDefaultConstructorRef1 x(i); // control check QSharedPointer ptr = QSharedPointer::create(i); QCOMPARE(ptr->i, i); QCOMPARE(&ptr->i, &i); } { NoDefaultConstructorRRef1(std::move(i)); // control check QSharedPointer ptr = QSharedPointer::create(std::move(i)); QCOMPARE(ptr->i, i); } { NoDefaultConstructorRRef2(std::unique_ptr(new int(1))); // control check QSharedPointer ptr = QSharedPointer::create(std::unique_ptr(new int(1))); QCOMPARE(*ptr->i, 1); std::unique_ptr p(new int(i)); ptr = QSharedPointer::create(std::move(p)); QCOMPARE(*ptr->i, i); } { QString text("Hello, World"); NoDefaultConstructorRef2(text, 1); // control check QSharedPointer ptr = QSharedPointer::create(text, 1); QCOMPARE(ptr->str, text); QCOMPARE(ptr->i, 1); } { QSharedPointer ptr; NoDefaultConstructorConstRef2(QLatin1String("string"), 1); // control check ptr = QSharedPointer::create(QLatin1String("string"), 1); QCOMPARE(ptr->str, QString("string")); QCOMPARE(ptr->i, 1); NoDefaultConstructorConstRef2(QByteArray("bytearray")); // control check ptr = QSharedPointer::create(QByteArray("bytearray")); QCOMPARE(ptr->str, QString("bytearray")); QCOMPARE(ptr->i, 42); } } void tst_QSharedPointer::creatingQObject() { { QSharedPointer ptr = QSharedPointer::create(); QCOMPARE(ptr->metaObject(), &QObject::staticMetaObject); QPointer qptr = ptr.data(); ptr.clear(); QVERIFY(qptr.isNull()); } safetyCheck(); { QSharedPointer ptr = QSharedPointer::create(); QCOMPARE(ptr->metaObject(), &OtherObject::staticMetaObject); } safetyCheck(); } void tst_QSharedPointer::mixTrackingPointerCode() { { // pointer created with tracking // deleted in code without tracking QSharedPointer ptr = QSharedPointer(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() { ++ptr; } void ref() { // if we're called after the destructor, we'll crash ptr->ref(); } }; class StrongThread: public QThread { protected: void run() { usleep(QRandomGenerator::global()->bounded(2000)); ptr->ref(); ptr.clear(); } public: QSharedPointer ptr; }; class WeakThread: public QThread { protected: void run() { usleep(QRandomGenerator::global()->bounded(2000)); QSharedPointer ptr = weak; if (ptr) ptr->ref(); ptr.clear(); } public: QWeakPointer weak; }; void tst_QSharedPointer::threadStressTest_data() { QTest::addColumn("strongThreadCount"); QTest::addColumn("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) { QVector allThreads(6 * qMax(strongThreadCount, weakThreadCount) + 3, 0); QSharedPointer base = QSharedPointer(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.count(); ++i) if (allThreads[i]) allThreads[i]->start(); // wait for them to finish for (int i = 0; i < allThreads.count(); ++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 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()); } c.insertMulti(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, int>, true>(); } void tst_QSharedPointer::hash() { hashAndMapTest, int>, false>(); } void tst_QSharedPointer::validConstructs() { { Data *aData = new Data; QSharedPointer ptr1 = QSharedPointer(aData); ptr1 = ptr1; // valid QSharedPointer ptr2(ptr1); ptr1 = ptr2; ptr2 = ptr1; ptr1 = QSharedPointer(); ptr1 = ptr2; } } typedef bool (QTest::QExternalTest:: * TestFunction)(const QByteArray &body); Q_DECLARE_METATYPE(TestFunction) void tst_QSharedPointer::invalidConstructs_data() { QTest::addColumn("testFunction"); QTest::addColumn("code"); QTest::newRow("sanity-checking") << &QTest::QExternalTest::tryCompile << ""; // QSharedPointer is not allowed QTest::newRow("void") << &QTest::QExternalTest::tryCompileFail << "QSharedPointer ptr;"; // implicit initialization QTest::newRow("implicit-initialization1") << &QTest::QExternalTest::tryCompileFail << "QSharedPointer ptr = new Data;"; QTest::newRow("implicit-initialization2") << &QTest::QExternalTest::tryCompileFail << "QSharedPointer ptr;" "ptr = new Data;"; QTest::newRow("implicit-initialization3") << &QTest::QExternalTest::tryCompileFail << "QWeakPointer ptr = new Data;"; QTest::newRow("implicit-initialization4") << &QTest::QExternalTest::tryCompileFail << "QWeakPointer ptr;" "ptr = new Data;"; // use of forward-declared class QTest::newRow("creating-forward-declaration") << &QTest::QExternalTest::tryCompileFail << "QSharedPointer::create();"; // upcast without cast operator: QTest::newRow("upcast1") << &QTest::QExternalTest::tryCompileFail << "QSharedPointer baseptr = QSharedPointer(new DerivedData);\n" "QSharedPointer ptr(baseptr);"; QTest::newRow("upcast2") << &QTest::QExternalTest::tryCompileFail << "QSharedPointer baseptr = QSharedPointer(new DerivedData);\n" "QSharedPointer ptr;\n" "ptr = baseptr;"; // dropping of const QTest::newRow("const-dropping1") << &QTest::QExternalTest::tryCompileFail << "QSharedPointer baseptr = QSharedPointer(new Data);\n" "QSharedPointer ptr(baseptr);"; QTest::newRow("const-dropping2") << &QTest::QExternalTest::tryCompileFail << "QSharedPointer baseptr = QSharedPointer(new Data);\n" "QSharedPointer ptr;" "ptr = baseptr;"; QTest::newRow("const-dropping-static-cast") << &QTest::QExternalTest::tryCompileFail << "QSharedPointer baseptr = QSharedPointer(new Data);\n" "qSharedPointerCast(baseptr);"; #ifndef QTEST_NO_RTTI QTest::newRow("const-dropping-dynamic-cast") << &QTest::QExternalTest::tryCompileFail << "QSharedPointer baseptr = QSharedPointer(new Data);\n" "qSharedPointerDynamicCast(baseptr);"; #endif QTest::newRow("const-dropping-object-cast1") << &QTest::QExternalTest::tryCompileFail << "QSharedPointer baseptr = QSharedPointer(new QObject);\n" "qSharedPointerObjectCast(baseptr);"; QTest::newRow("const-dropping-object-cast2") << &QTest::QExternalTest::tryCompileFail << "QSharedPointer baseptr = QSharedPointer(new QObject);\n" "qobject_cast(baseptr);"; // arithmethics through automatic cast operators QTest::newRow("arithmethic1") << &QTest::QExternalTest::tryCompileFail << "QSharedPointer a;" "QSharedPointer b;\n" "if (a == b) return;"; QTest::newRow("arithmethic2") << &QTest::QExternalTest::tryCompileFail << "QSharedPointer a;" "QSharedPointer 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 ptr1 = QSharedPointer(aData);\n" "QSharedPointer ptr2 = QSharedPointer(aData);\n"; // two QObjects with the same pointer QTest::newRow("same-pointer-to-qobject") << &QTest::QExternalTest::tryRunFail << "QObject *anObj = new QObject;\n" "QSharedPointer ptr1 = QSharedPointer(anObj);\n" "QSharedPointer ptr2 = QSharedPointer(anObj);\n"; // re-creation: QTest::newRow("re-creation") << &QTest::QExternalTest::tryRunFail << "Data *aData = new Data;\n" "QSharedPointer ptr1 = QSharedPointer(aData);" "ptr1 = QSharedPointer(aData);"; // any type of cast for unrelated types: // (we have no reinterpret_cast) QTest::newRow("invalid-cast1") << &QTest::QExternalTest::tryCompileFail << "QSharedPointer ptr1;\n" "QSharedPointer ptr2 = qSharedPointerCast(ptr1);"; #ifndef QTEST_NO_RTTI QTest::newRow("invalid-cast2") << &QTest::QExternalTest::tryCompileFail << "QSharedPointer ptr1;\n" "QSharedPointer ptr2 = qSharedPointerDynamicCast(ptr1);"; #endif QTest::newRow("invalid-cast3") << &QTest::QExternalTest::tryCompileFail << "QSharedPointer ptr1;\n" "QSharedPointer ptr2 = qSharedPointerConstCast(ptr1);"; QTest::newRow("invalid-cast4") << &QTest::QExternalTest::tryCompileFail << "QSharedPointer ptr1;\n" "QSharedPointer ptr2 = qSharedPointerObjectCast(ptr1);"; QTest::newRow("weak-pointer-from-regular-pointer") << &QTest::QExternalTest::tryCompileFail << "Data *ptr = 0;\n" "QWeakPointer weakptr(ptr);\n"; QTest::newRow("shared-pointer-implicit-from-uninitialized") << &QTest::QExternalTest::tryCompileFail << "Data *ptr = 0;\n" "QSharedPointer weakptr = Qt::Uninitialized;\n"; QTest::newRow("incompatible-custom-deleter1") << &QTest::QExternalTest::tryCompileFail << "extern void incompatibleCustomDeleter(int *);\n" "QSharedPointer ptr(new Data, incompatibleCustomDeleter);\n"; QTest::newRow("incompatible-custom-deleter2") << &QTest::QExternalTest::tryCompileFail << "struct IncompatibleCustomDeleter { void operator()(int *); };\n" "QSharedPointer ptr(new Data, IncompatibleCustomDeleter());\n"; QTest::newRow("incompatible-custom-lambda-deleter") << &QTest::QExternalTest::tryCompileFail << "QSharedPointer ptr(new Data, [](int *) {});\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 \n" "#include \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 baseptr; QSharedPointer 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"); } } void tst_QSharedPointer::qvariantCast() { QSharedPointer sp = QSharedPointer::create(); sp->setObjectName("A test name"); QVariant v = QVariant::fromValue(sp); { QSharedPointer other = qSharedPointerFromVariant(v); QCOMPARE(other->objectName(), QString::fromLatin1("A test name")); } { QSharedPointer other = qSharedPointerFromVariant(v); QCOMPARE(other->objectName(), QString::fromLatin1("A test name")); } { QSharedPointer other = qSharedPointerFromVariant(v); QCOMPARE(other->objectName(), QString::fromLatin1("A test name")); } { QSharedPointer other = qSharedPointerFromVariant(v); QVERIFY(!other); } // Intentionally does not compile. // QSharedPointer sop = qSharedPointerFromVariant(v); #if QT_DEPRECATED_SINCE(5, 0) v = QVariant::fromValue(sp.toWeakRef()); { QWeakPointer other = qWeakPointerFromVariant(v); QCOMPARE(other.data()->objectName(), QString::fromLatin1("A test name")); } { QWeakPointer other = qWeakPointerFromVariant(v); QCOMPARE(other.data()->objectName(), QString::fromLatin1("A test name")); } { QWeakPointer other = qWeakPointerFromVariant(v); QCOMPARE(other.data()->objectName(), QString::fromLatin1("A test name")); } { QWeakPointer other = qWeakPointerFromVariant(v); QVERIFY(!other); } // Intentionally does not compile. // QWeakPointer sop = qWeakPointerFromVariant(v); QFile file; QWeakPointer tracking = &file; tracking.data()->setObjectName("A test name"); v = QVariant::fromValue(tracking); { QWeakPointer other = qWeakPointerFromVariant(v); QCOMPARE(other.data()->objectName(), QString::fromLatin1("A test name")); } { QWeakPointer other = qWeakPointerFromVariant(v); QCOMPARE(other.data()->objectName(), QString::fromLatin1("A test name")); } { QWeakPointer other = qWeakPointerFromVariant(v); QCOMPARE(other.data()->objectName(), QString::fromLatin1("A test name")); } { QWeakPointer other = qWeakPointerFromVariant(v); QVERIFY(!other); } #endif } class SomeClass : public QEnableSharedFromThis { public: SomeClass() { } QSharedPointer getSharedPtr() { return sharedFromThis(); } QSharedPointer getSharedPtr() const { return sharedFromThis(); } Data data; }; void tst_QSharedPointer::sharedFromThis() { const int generations = Data::generationCounter; const int destructions = Data::destructorCounter; { SomeClass sc; QSharedPointer scp = sc.sharedFromThis(); QVERIFY(scp.isNull()); QCOMPARE(Data::generationCounter, generations + 1); QCOMPARE(Data::destructorCounter, destructions); QSharedPointer const_scp = sc.sharedFromThis(); QVERIFY(const_scp.isNull()); QCOMPARE(Data::generationCounter, generations + 1); QCOMPARE(Data::destructorCounter, destructions); QWeakPointer wcp = sc.sharedFromThis(); QVERIFY(wcp.isNull()); QCOMPARE(Data::generationCounter, generations + 1); QCOMPARE(Data::destructorCounter, destructions); QWeakPointer 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_scp = sc.sharedFromThis(); QVERIFY(const_scp.isNull()); QCOMPARE(Data::generationCounter, generations + 2); QCOMPARE(Data::destructorCounter, destructions + 1); QWeakPointer 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 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(sc); QVERIFY(!scp.isNull()); QCOMPARE(scp.data(), sc); QCOMPARE(Data::generationCounter, generations + 3); QCOMPARE(Data::destructorCounter, destructions + 2); QSharedPointer 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 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 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 scp5; QVERIFY(scp5.isNull()); QCOMPARE(Data::generationCounter, generations + 3); QCOMPARE(Data::destructorCounter, destructions + 2); scp5 = const_cast(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 scp; QVERIFY(scp.isNull()); { QSharedPointer 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 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 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 scp2; scp2 = qSharedPointerConstCast(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 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 scp2 = QSharedPointer::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 scp2(new SomeClass()); QVERIFY(!scp2.isNull()); QCOMPARE(Data::generationCounter, generations + 6); QCOMPARE(Data::destructorCounter, destructions + 5); QWeakPointer wcp2(scp2.constCast()); 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 ptr; QVERIFY_EXCEPTION_THROWN(ptr = QSharedPointer::create(), QString); 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 getB() = 0; }; struct B: public IB { IA *m_a; B(IA *a_a) :m_a(a_a) { } ~B() { QSharedPointer b = m_a->getB(); } }; struct A: public IA { QSharedPointer b; virtual QSharedPointer getB() { return b; } A() { b = QSharedPointer(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; } QTEST_MAIN(tst_QSharedPointer) #include "tst_qsharedpointer.moc"