/**************************************************************************** ** ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). ** Contact: Nokia Corporation (qt-info@nokia.com) ** ** This file is part of the test suite of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** No Commercial Usage ** This file contains pre-release code and may not be distributed. ** You may use this file in accordance with the terms and conditions ** contained in the either Technology Preview License Agreement or the ** Beta Release License Agreement. ** ** GNU Lesser General Public License Usage ** Alternatively, 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.0, 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. ** ** If you are unsure which license is appropriate for your use, please ** contact the sales department at http://www.qtsoftware.com/contact. ** $QT_END_LICENSE$ ** ****************************************************************************/ #define QT_SHAREDPOINTER_TRACK_POINTERS #include "qsharedpointer.h" #include #include #include #include "externaltests.h" #include "wrapper.h" #include #include namespace QtSharedPointer { Q_CORE_EXPORT void internalSafetyCheckCleanCheck(); } class tst_QSharedPointer: public QObject { Q_OBJECT private slots: void basics_data(); void basics(); void forwardDeclaration1(); void forwardDeclaration2(); void memoryManagement(); void downCast(); void upCast(); void qobjectWeakManagement(); void noSharedPointerFromWeakQObject(); 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 threadStressTest_data(); void threadStressTest(); void validConstructs(); void invalidConstructs_data(); void invalidConstructs(); public slots: void cleanup() { check(); } public: inline void check() { #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() { Q_ASSERT_X(generation > 0, "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 == 1); QVERIFY(!refCountData(ptr) || refCountData(ptr)->strongref == 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 == 1); QVERIFY(!refCountData(ptr) || refCountData(ptr)->strongref == 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 == 1); QVERIFY(!refCountData(ptr) || refCountData(ptr)->strongref == 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 == 1); QVERIFY(!refCountData(ptr) || refCountData(ptr)->strongref == 1); // aData is deleted here } class ForwardDeclared; ForwardDeclared *forwardPointer(); void externalForwardDeclaration(); extern int forwardDeclaredDestructorRunCount; void tst_QSharedPointer::forwardDeclaration1() { #if defined(Q_CC_SUN) QSKIP("This type of forward declaration is not valid with this compiler", SkipAll); #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 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), 1); QCOMPARE(int(refCountData(baseptr)->strongref), 1); { QWeakPointer derivedptr = qWeakPointerCast(baseptr); QVERIFY(baseptr == derivedptr); } QCOMPARE(int(refCountData(baseptr)->weakref), 1); QCOMPARE(int(refCountData(baseptr)->strongref), 1); { QWeakPointer weakptr = baseptr; QSharedPointer derivedptr = qSharedPointerCast(weakptr); QVERIFY(baseptr == derivedptr); QCOMPARE(static_cast(derivedptr.data()), baseptr.data()); } QCOMPARE(int(refCountData(baseptr)->weakref), 1); QCOMPARE(int(refCountData(baseptr)->strongref), 1); { QSharedPointer derivedptr = baseptr.staticCast(); QVERIFY(baseptr == derivedptr); QCOMPARE(static_cast(derivedptr.data()), baseptr.data()); } QCOMPARE(int(refCountData(baseptr)->weakref), 1); QCOMPARE(int(refCountData(baseptr)->strongref), 1); } class OtherObject: public QObject { Q_OBJECT }; void tst_QSharedPointer::qobjectWeakManagement() { { QObject *obj = new QObject; QWeakPointer weak(obj); QVERIFY(!weak.isNull()); QVERIFY(weak.data() == obj); // now delete delete obj; QVERIFY(weak.isNull()); } check(); { // 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()); } check(); { // 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()); } check(); { // 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()); } check(); { // 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()); } check(); { // 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()); } check(); } void tst_QSharedPointer::noSharedPointerFromWeakQObject() { // you're not allowed to create a QSharedPointer from an unmanaged QObject QObject obj; QWeakPointer weak(&obj); QSharedPointer strong = weak.toStrongRef(); QVERIFY(strong.isNull()); // is something went wrong, we'll probably crash here } 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); #ifndef QT_NO_PARTIAL_TEMPLATE_SPECIALIZATION // again: ptr = qobject_cast(baseptr); QVERIFY(ptr == data); // again: ptr = qobject_cast >(baseptr); QVERIFY(ptr == data); #endif } check(); { 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); #ifndef QT_NO_PARTIAL_TEMPLATE_SPECIALIZATION // again: ptr = qobject_cast(baseptr); QVERIFY(ptr == data); // again: ptr = qobject_cast >(baseptr); QVERIFY(ptr == data); #endif } check(); { OtherObject *data = new OtherObject; QPointer qptr = data; 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(qptr.isNull()); QVERIFY(weakptr.toStrongRef().isNull()); // verify that the object casts fail without crash QSharedPointer otherptr = qSharedPointerObjectCast(weakptr); QVERIFY(otherptr.isNull()); #ifndef QT_NO_PARTIAL_TEMPLATE_SPECIALIZATION // again: otherptr = qobject_cast(weakptr); QVERIFY(otherptr.isNull()); // again: otherptr = qobject_cast >(weakptr); QVERIFY(otherptr.isNull()); #endif } check(); } void tst_QSharedPointer::differentPointers() { { DiffPtrDerivedData *aData = new DiffPtrDerivedData; Data *aBase = aData; Q_ASSERT(aData == aBase); Q_ASSERT(*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); } check(); { DiffPtrDerivedData *aData = new DiffPtrDerivedData; Data *aBase = aData; Q_ASSERT(aData == aBase); Q_ASSERT(*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); } check(); { DiffPtrDerivedData *aData = new DiffPtrDerivedData; Data *aBase = aData; Q_ASSERT(aData == aBase); Q_ASSERT(*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); } check(); } void tst_QSharedPointer::virtualBaseDifferentPointers() { { VirtualDerived *aData = new VirtualDerived; Data *aBase = aData; Q_ASSERT(aData == aBase); Q_ASSERT(*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); } check(); { VirtualDerived *aData = new VirtualDerived; Data *aBase = aData; Q_ASSERT(aData == aBase); Q_ASSERT(*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); } check(); } #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), 1); QCOMPARE(int(refCountData(baseptr)->strongref), 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), 1); QCOMPARE(int(refCountData(baseptr)->strongref), 1); { QSharedPointer derivedptr = baseptr.dynamicCast(); QVERIFY(baseptr == derivedptr); QCOMPARE(derivedptr.data(), aData); QCOMPARE(static_cast(derivedptr.data()), baseptr.data()); } QCOMPARE(int(refCountData(baseptr)->weakref), 1); QCOMPARE(int(refCountData(baseptr)->strongref), 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), 1); QCOMPARE(int(refCountData(baseptr)->strongref), 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), 1); QCOMPARE(int(refCountData(baseptr)->strongref), 1); { QSharedPointer derivedptr = baseptr.dynamicCast(); QVERIFY(baseptr == derivedptr); QCOMPARE(derivedptr.data(), aData); QCOMPARE(static_cast(derivedptr.data()), baseptr.data()); } QCOMPARE(int(refCountData(baseptr)->weakref), 1); QCOMPARE(int(refCountData(baseptr)->strongref), 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), 1); QCOMPARE(int(refCountData(baseptr)->strongref), 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), 1); QCOMPARE(int(refCountData(baseptr)->strongref), 1); { QSharedPointer derivedptr = baseptr.dynamicCast(); QVERIFY(baseptr == derivedptr); QCOMPARE(derivedptr.data(), aData); QCOMPARE(static_cast(derivedptr.data()), baseptr.data()); } QCOMPARE(int(refCountData(baseptr)->weakref), 1); QCOMPARE(int(refCountData(baseptr)->strongref), 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), 1); QCOMPARE(int(refCountData(baseptr)->strongref), 1); { QSharedPointer derivedptr = baseptr.dynamicCast(); QVERIFY(derivedptr.isNull()); } QCOMPARE(int(refCountData(baseptr)->weakref), 1); QCOMPARE(int(refCountData(baseptr)->strongref), 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(); #if !defined(Q_CC_HPACC) && !defined(QT_ARCH_PARISC) // the aCC series 3 compiler we have on the PA-RISC // machine crashes compiling this code QSharedPointer cvptr(ptr); QSharedPointer cvptr2(cptr); QSharedPointer cvptr3(vptr); cvptr = ptr; cvptr2 = cptr; cvptr3 = vptr; ptr = qSharedPointerConstCast(cvptr); ptr = cvptr.constCast(); #endif } check(); { Data *aData = new Data; QSharedPointer ptr = QSharedPointer(aData); const QSharedPointer cptr = ptr; ptr = cptr; QSharedPointer other = qSharedPointerCast(cptr); other = qSharedPointerDynamicCast(cptr); QCOMPARE(cptr.data(), aData); QCOMPARE(cptr.operator->(), aData); } check(); } 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); } check(); { QSharedPointer ptr(new DerivedData, &Data::doDelete); QSharedPointer ptr2(new DerivedData, &Data::alsoDelete); QSharedPointer ptr3(new DerivedData, &Data::virtualDelete); } check(); customDeleterFnCallCount = 0; { QSharedPointer ptr = QSharedPointer(new Data, customDeleterFn); ptr.data(); QCOMPARE(customDeleterFnCallCount, 0); } QCOMPARE(customDeleterFnCallCount, 1); check(); customDeleterFnCallCount = 0; { QSharedPointer ptr = QSharedPointer(new Data, customDeleterFn); QCOMPARE(customDeleterFnCallCount, 0); ptr.clear(); QCOMPARE(customDeleterFnCallCount, 1); } QCOMPARE(customDeleterFnCallCount, 1); check(); customDeleterFnCallCount = 0; { QSharedPointer ptr = QSharedPointer(new Data, customDeleterFn); QCOMPARE(customDeleterFnCallCount, 0); ptr = QSharedPointer(new Data); QCOMPARE(customDeleterFnCallCount, 1); } QCOMPARE(customDeleterFnCallCount, 1); check(); customDeleterFnCallCount = 0; { QSharedPointer ptr = QSharedPointer(new DerivedData, customDeleterFn); ptr.data(); QCOMPARE(customDeleterFnCallCount, 0); } QCOMPARE(customDeleterFnCallCount, 1); check(); customDeleterFnCallCount = 0; { QSharedPointer ptr = QSharedPointer(new DerivedData, customDeleterFn); ptr.data(); QCOMPARE(customDeleterFnCallCount, 0); } QCOMPARE(customDeleterFnCallCount, 1); check(); customDeleterFnCallCount = 0; { QSharedPointer other; { QSharedPointer ptr = QSharedPointer(new Data, customDeleterFn); other = ptr; QCOMPARE(customDeleterFnCallCount, 0); } QCOMPARE(customDeleterFnCallCount, 0); } QCOMPARE(customDeleterFnCallCount, 1); check(); customDeleterFnCallCount = 0; { QSharedPointer other; { QSharedPointer ptr = QSharedPointer(new DerivedData, customDeleterFn); other = ptr; QCOMPARE(customDeleterFnCallCount, 0); } QCOMPARE(customDeleterFnCallCount, 0); } QCOMPARE(customDeleterFnCallCount, 1); check(); 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); check(); 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); check(); 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); check(); dataDeleter.callCount = 0; { QSharedPointer ptr = QSharedPointer(new DerivedData, dataDeleter); ptr.data(); QCOMPARE(dataDeleter.callCount, 0); } QCOMPARE(dataDeleter.callCount, 1); QCOMPARE(refcount, 1); check(); 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); check(); 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); check(); 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); check(); } 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); } check(); 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 == 1); QVERIFY(d->strongref == 0); } check(); 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); } check(); { 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); } check(); { QSharedPointer ptr = QSharedPointer::create(); QCOMPARE(ptr->classLevel(), 4); QCOMPARE(ptr->moreData, 0xc0ffee); QSharedPointer baseptr = ptr; QCOMPARE(baseptr->classLevel(), 4); } check(); } void tst_QSharedPointer::creatingQObject() { { QSharedPointer ptr = QSharedPointer::create(); QCOMPARE(ptr->metaObject(), &QObject::staticMetaObject); QPointer qptr = ptr.data(); ptr.clear(); QVERIFY(qptr.isNull()); } check(); { QSharedPointer ptr = QSharedPointer::create(); QCOMPARE(ptr->metaObject(), &OtherObject::staticMetaObject); } check(); } void tst_QSharedPointer::mixTrackingPointerCode() { { // pointer created with tracking // deleted in code without tracking QSharedPointer ptr = QSharedPointer(new int(42)); Wrapper w(ptr); ptr.clear(); } check(); { // 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; QTest::newRow("5+10") << 5 << 10; QTest::newRow("5+30") << 5 << 30; #ifndef Q_OS_WINCE 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 = 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 >= minValue); QVERIFY(counter <= maxValue); } } 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") << &QTest::QExternalTest::tryRun << "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);"; #ifndef QT_NO_PARTIAL_TEMPLATE_SPECIALIZATION QTest::newRow("const-dropping-object-cast2") << &QTest::QExternalTest::tryCompileFail << "QSharedPointer baseptr = QSharedPointer(new QObject);\n" "qobject_cast(baseptr);"; #endif // 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;"; #if QT_VERSION >= 0x040600 // 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);"; #endif // 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-from-unmanaged-qobject") << &QTest::QExternalTest::tryRunFail << "QObject *ptr = new QObject;\n" "QWeakPointer weak = ptr;\n" // this makes the object unmanaged "QSharedPointer shared(ptr);\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.'", SkipAll); #endif #ifdef QTEST_CROSS_COMPILED QSKIP("This test does not work on cross compiled systems", SkipAll); #endif QTest::QExternalTest test; test.setDebugMode(true); test.setQtModules(QTest::QExternalTest::QtCore); test.setExtraProgramSources(QStringList() << SRCDIR "forwarddeclared.cpp"); test.setProgramHeader( "#define QT_SHAREDPOINTER_TRACK_POINTERS\n" "#include \n" "#include \n" "\n" "struct Data { int i; };\n" "struct DerivedData: public Data { int j; };\n" "\n" "extern int forwardDeclaredDestructorRunCount;\n" "struct 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:"); printf("%s\n", test.standardError().constData()); } if (!result) { qWarning("External code testing failed\nCode:\n%s\n", body.constData()); QFAIL("Fail"); } } QTEST_MAIN(tst_QSharedPointer) #include "tst_qsharedpointer.moc"