/**************************************************************************** ** ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/ ** ** This file is part of the test suite of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** GNU Lesser General Public License Usage ** This file may be used under the terms of the GNU Lesser General Public ** License version 2.1 as published by the Free Software Foundation and ** appearing in the file LICENSE.LGPL included in the packaging of this ** file. Please review the following information to ensure the GNU Lesser ** General Public License version 2.1 requirements will be met: ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Nokia gives you certain additional ** rights. These rights are described in the Nokia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU General ** Public License version 3.0 as published by the Free Software Foundation ** and appearing in the file LICENSE.GPL included in the packaging of this ** file. Please review the following information to ensure the GNU General ** Public License version 3.0 requirements will be met: ** http://www.gnu.org/copyleft/gpl.html. ** ** Other Usage ** Alternatively, this file may be used in accordance with the terms and ** conditions contained in a signed written agreement between you and Nokia. ** ** ** ** ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #define QT_SHAREDPOINTER_TRACK_POINTERS #include "qsharedpointer.h" #include #include #include #include #include #include "externaltests.h" #include "wrapper.h" #include #include QT_BEGIN_NAMESPACE namespace QtSharedPointer { Q_CORE_EXPORT void internalSafetyCheckCleanCheck(); } QT_END_NAMESPACE class tst_QSharedPointer: public QObject { Q_OBJECT private slots: void basics_data(); void basics(); void operators(); void swap(); void forwardDeclaration1(); void forwardDeclaration2(); void memoryManagement(); void downCast(); void functionCallDownCast(); void upCast(); void qobjectWeakManagement(); void noSharedPointerFromWeakQObject(); void sharedPointerFromQObjectWithWeak(); void weakQObjectFromSharedPointer(); void objectCast(); void differentPointers(); void virtualBaseDifferentPointers(); #ifndef QTEST_NO_RTTI void dynamicCast(); void dynamicCastDifferentPointers(); void dynamicCastVirtualBase(); void dynamicCastFailure(); #endif void constCorrectness(); void customDeleter(); void creating(); void creatingQObject(); void mixTrackingPointerCode(); void reentrancyWhileDestructing(); void threadStressTest_data(); void threadStressTest(); void map(); void hash(); void validConstructs(); void invalidConstructs_data(); void invalidConstructs(); public slots: void cleanup() { safetyCheck(); } public: inline void safetyCheck() { #ifdef QT_BUILD_INTERNAL QtSharedPointer::internalSafetyCheckCleanCheck(); #endif } }; template class RefCountHack: public Base { public: using Base::d; }; template static inline QtSharedPointer::ExternalRefCountData *refCountData(const Base &b) { return static_cast *>(&b)->d; } 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; 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*)); } 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.operator->(), aData); 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.load() == 1); QVERIFY(!refCountData(ptr) || refCountData(ptr)->strongref.load() == 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.load() == 1); QVERIFY(!refCountData(ptr) || refCountData(ptr)->strongref.load() == 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); QVERIFY(copy == aData); } QVERIFY(!refCountData(ptr) || refCountData(ptr)->weakref.load() == 1); QVERIFY(!refCountData(ptr) || refCountData(ptr)->strongref.load() == 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); } QVERIFY(!refCountData(ptr) || refCountData(ptr)->weakref.load() == 1); QVERIFY(!refCountData(ptr) || refCountData(ptr)->strongref.load() == 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(diff != 0); // operator- QCOMPARE(p2 - p1.data(), diff); QCOMPARE(p2.data() - 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.data() < p2); QVERIFY(p1 < p2); QVERIFY(!(p2 < p1)); QVERIFY(!(p2 < p2)); QVERIFY(!(p1 < p1)); // qHash QCOMPARE(qHash(p1), qHash(p1.data())); QCOMPARE(qHash(p2), qHash(p2.data())); } 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); } class ForwardDeclared; ForwardDeclared *forwardPointer(); void externalForwardDeclaration(); extern int forwardDeclaredDestructorRunCount; void tst_QSharedPointer::forwardDeclaration1() { #if defined(Q_CC_SUN) || defined(Q_CC_WINSCW) || defined(Q_CC_RVCT) QSKIP("This type of forward declaration is not valid with this compiler"); #else externalForwardDeclaration(); struct Wrapper { QSharedPointer pointer; }; forwardDeclaredDestructorRunCount = 0; { Wrapper w; w.pointer = QSharedPointer(forwardPointer()); QVERIFY(!w.pointer.isNull()); } QCOMPARE(forwardDeclaredDestructorRunCount, 1); #endif } #include "forwarddeclared.h" void tst_QSharedPointer::forwardDeclaration2() { forwardDeclaredDestructorRunCount = 0; { struct Wrapper { QSharedPointer pointer; }; Wrapper w1, w2; w1.pointer = QSharedPointer(forwardPointer()); QVERIFY(!w1.pointer.isNull()); } QCOMPARE(forwardDeclaredDestructorRunCount, 1); } 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); } 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.load()), 1); QCOMPARE(int(refCountData(baseptr)->strongref.load()), 1); { QWeakPointer derivedptr = qWeakPointerCast(baseptr); QVERIFY(baseptr == derivedptr); } QCOMPARE(int(refCountData(baseptr)->weakref.load()), 1); QCOMPARE(int(refCountData(baseptr)->strongref.load()), 1); { QWeakPointer weakptr = baseptr; QSharedPointer derivedptr = qSharedPointerCast(weakptr); QVERIFY(baseptr == derivedptr); QCOMPARE(static_cast(derivedptr.data()), baseptr.data()); } QCOMPARE(int(refCountData(baseptr)->weakref.load()), 1); QCOMPARE(int(refCountData(baseptr)->strongref.load()), 1); { QSharedPointer derivedptr = baseptr.staticCast(); QVERIFY(baseptr == derivedptr); QCOMPARE(static_cast(derivedptr.data()), baseptr.data()); } QCOMPARE(int(refCountData(baseptr)->weakref.load()), 1); QCOMPARE(int(refCountData(baseptr)->strongref.load()), 1); } class OtherObject: public QObject { Q_OBJECT }; void tst_QSharedPointer::qobjectWeakManagement() { { 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(); } 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()); } void tst_QSharedPointer::weakQObjectFromSharedPointer() { // 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()); } 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()); // 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::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.load()), 1); QCOMPARE(int(refCountData(baseptr)->strongref.load()), 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.load()), 1); QCOMPARE(int(refCountData(baseptr)->strongref.load()), 1); { QSharedPointer derivedptr = baseptr.dynamicCast(); QVERIFY(baseptr == derivedptr); QCOMPARE(derivedptr.data(), aData); QCOMPARE(static_cast(derivedptr.data()), baseptr.data()); } QCOMPARE(int(refCountData(baseptr)->weakref.load()), 1); QCOMPARE(int(refCountData(baseptr)->strongref.load()), 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.load()), 1); QCOMPARE(int(refCountData(baseptr)->strongref.load()), 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.load()), 1); QCOMPARE(int(refCountData(baseptr)->strongref.load()), 1); { QSharedPointer derivedptr = baseptr.dynamicCast(); QVERIFY(baseptr == derivedptr); QCOMPARE(derivedptr.data(), aData); QCOMPARE(static_cast(derivedptr.data()), baseptr.data()); } QCOMPARE(int(refCountData(baseptr)->weakref.load()), 1); QCOMPARE(int(refCountData(baseptr)->strongref.load()), 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.load()), 1); QCOMPARE(int(refCountData(baseptr)->strongref.load()), 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.load()), 1); QCOMPARE(int(refCountData(baseptr)->strongref.load()), 1); { QSharedPointer derivedptr = baseptr.dynamicCast(); QVERIFY(baseptr == derivedptr); QCOMPARE(derivedptr.data(), aData); QCOMPARE(static_cast(derivedptr.data()), baseptr.data()); } QCOMPARE(int(refCountData(baseptr)->weakref.load()), 1); QCOMPARE(int(refCountData(baseptr)->strongref.load()), 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.load()), 1); QCOMPARE(int(refCountData(baseptr)->strongref.load()), 1); { QSharedPointer derivedptr = baseptr.dynamicCast(); QVERIFY(derivedptr.isNull()); } QCOMPARE(int(refCountData(baseptr)->weakref.load()), 1); QCOMPARE(int(refCountData(baseptr)->strongref.load()), 1); } #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(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(); } 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.load() == 1); QVERIFY(d->strongref.load() == 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::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(rand() % 2000); ptr->ref(); ptr.clear(); } public: QSharedPointer ptr; }; class WeakThread: public QThread { protected: void run() { usleep(rand() % 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; #ifndef Q_OS_WINCE // Windows CE cannot run this many threads QTest::newRow("5+10") << 5 << 10; QTest::newRow("5+30") << 5 << 30; QTest::newRow("100+100") << 100 << 100; #endif } 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.store(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(); #ifdef Q_OS_WINCE srand(QDateTime::currentDateTime().toTime_t()); #else srand(time(NULL)); #endif // 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.load() >= minValue); QVERIFY(counter.load() <= 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; 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("forward-declaration") #ifdef Q_CC_CLANG // Deleting a forward declaration is undefined, which results in a linker error with clang << &QTest::QExternalTest::tryLinkFail #else // Other compilers accept the code, but do not call the destructor at run-time << &QTest::QExternalTest::tryRun #endif << "forwardDeclaredDestructorRunCount = 0;\n" "{ QSharedPointer ptr = QSharedPointer(forwardPointer()); }\n" "exit(forwardDeclaredDestructorRunCount);"; 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"; // 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"; } 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.setExtraProgramSources(QStringList() << QFINDTESTDATA("forwarddeclared.cpp")); 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" "extern int forwardDeclaredDestructorRunCount;\n" "class ForwardDeclared;\n" "ForwardDeclared *forwardPointer();\n" ); QFETCH(QString, code); static bool sane = true; if (code.isEmpty()) { static const char snippet[] = "QSharedPointer baseptr; QSharedPointer ptr;"; if (!test.tryCompile("") || !test.tryRun("") || !test.tryRunFail("exit(1);") || !test.tryCompile(snippet) || !test.tryLink(snippet) || !test.tryRun(snippet)) { 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 (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"); } } 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"