// Copyright (C) 2021 The Qt Company Ltd.
// Copyright (C) 2022 Intel Corporation.
// Copyright (C) 2021 Klarälvdalens Datakonsult AB.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only

#define QT_SHAREDPOINTER_TRACK_POINTERS
#include "qsharedpointer.h"
#include <QTest>
#include <QPointer>
#include <QRandomGenerator>
#include <QtCore/QHash>
#include <QtCore/QList>
#include <QtCore/QMap>
#include <QtCore/QThread>
#include <QtCore/private/qvolatile_p.h>

#include "forwarddeclared.h"
#include "nontracked.h"
#include "wrapper.h"

#include <array>
#include <memory>
#include <stdlib.h>
#include <time.h>

#if defined(Q_OS_UNIX) && !defined(Q_OS_INTEGRITY)
#include <sys/resource.h>
#endif

QT_BEGIN_NAMESPACE
namespace QtSharedPointer {
    Q_CORE_EXPORT void internalSafetyCheckCleanCheck();
}
QT_END_NAMESPACE

class tst_QSharedPointer: public QObject
{
    Q_OBJECT

private slots:
    void initTestCase();
    void basics_data();
    void basics();
    void operators();
    void nullptrOps();
    void swap();
    void moveSemantics();
    void useOfForwardDeclared();
    void memoryManagement();
    void dropLastReferenceOfForwardDeclared();
    void nonVirtualDestructors();
    void lock();
    void downCast();
    void functionCallDownCast();
    void upCast();
    void qobjectWeakManagement();
    void weakQObjectFromSharedPointer();
    void objectCast();
    void objectCastStdSharedPtr();
    void 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 customDeleterOnNullptr();
    void creating();
    void creatingCvQualified();
    void creatingVariadic();
    void creatingQObject();
    void mixTrackingPointerCode();
    void reentrancyWhileDestructing();
    void map();
    void hash();
    void qvariantCast();
    void sharedFromThis();

    void constructorThrow();
    void overloads();

    void threadStressTest_data();
    void threadStressTest();
    void validConstructs();
#if 0
    void invalidConstructs_data();
    void invalidConstructs();
#endif
    void ownerComparisons();

    // let invalidConstructs be the last test, because it's the slowest;
    // add new tests above this block
public slots:
    void cleanup() { safetyCheck(); }

public:
    inline void safetyCheck()
    {
#ifdef QT_BUILD_INTERNAL
        QtSharedPointer::internalSafetyCheckCleanCheck();
#endif
    }
};

void tst_QSharedPointer::initTestCase()
{
#if defined(Q_OS_UNIX) && !defined(Q_OS_INTEGRITY)
    // The tests create a lot of threads, which require file descriptors. On systems like
    // OS X low defaults such as 256 as the limit for the number of simultaneously
    // open files is not sufficient.
    struct rlimit numFiles;
    if (getrlimit(RLIMIT_NOFILE, &numFiles) == 0 && numFiles.rlim_cur < 1024) {
        numFiles.rlim_cur = qMin(rlim_t(1024), numFiles.rlim_max);
        setrlimit(RLIMIT_NOFILE, &numFiles);
    }
#endif
}

template<typename T> static inline
QtSharedPointer::ExternalRefCountData *refCountData(const QSharedPointer<T> &b)
{
    // access d-pointer:
    struct Dummy {
        void* value;
        QtSharedPointer::ExternalRefCountData* data;
    };
    // sanity checks:
    static_assert(sizeof(QSharedPointer<T>) == sizeof(Dummy));
    Q_ASSERT(static_cast<const Dummy*>(static_cast<const void*>(&b))->value == b.data());
    return static_cast<const Dummy*>(static_cast<const void*>(&b))->data;
}

class Data
{
public:
    static int destructorCounter;
    static int generationCounter;
    int generation;

    Data() : generation(++generationCounter)
    { }

    virtual ~Data()
    {
        if (generation <= 0)
            qFatal("tst_qsharedpointer: Double deletion!");
        generation = 0;
        ++destructorCounter;
    }

    void doDelete()
    {
        delete this;
    }

    bool alsoDelete()
    {
        doDelete();
        return true;
    }

    virtual void virtualDelete()
    {
        delete this;
    }

    virtual int classLevel() { return 1; }
};
int Data::generationCounter = 0;
int Data::destructorCounter = 0;

struct NoDefaultConstructor1
{
    int i;
    NoDefaultConstructor1(int i) : i(i) {}
    NoDefaultConstructor1(uint j) : i(j + 42) {}
};

struct NoDefaultConstructorRef1
{
    int &i;
    NoDefaultConstructorRef1(int &i) : i(i) {}
};

struct NoDefaultConstructor2
{
    void *ptr;
    int i;
    NoDefaultConstructor2(void *ptr, int i) : ptr(ptr), i(i) {}
};

struct NoDefaultConstructorRef2
{
    QString str;
    int i;
    NoDefaultConstructorRef2(QString &str, int i) : str(str), i(i) {}
};

struct NoDefaultConstructorConstRef2
{
    QString str;
    int i;
    NoDefaultConstructorConstRef2(const QString &str, int i) : str(str), i(i) {}
    NoDefaultConstructorConstRef2(const QByteArray &ba, int i = 42) : str(QString::fromLatin1(ba)), i(i) {}
};

struct NoDefaultConstructorRRef1
{
    int &i;
    NoDefaultConstructorRRef1(int &&i) : i(i) {}
};

struct NoDefaultConstructorRRef2
{
    std::unique_ptr<int> i;
    NoDefaultConstructorRRef2(std::unique_ptr<int> &&i) : i(std::move(i)) {}
};

void tst_QSharedPointer::basics_data()
{
    QTest::addColumn<bool>("isNull");
    QTest::newRow("null") << true;
    QTest::newRow("non-null") << false;
}

void tst_QSharedPointer::basics()
{
    {
        QSharedPointer<Data> ptr;
        QWeakPointer<Data> weakref;

        QCOMPARE(sizeof(ptr), 2*sizeof(void*));
        QCOMPARE(sizeof(weakref), 2*sizeof(void*));
    }

    {
        QSharedPointer<const Data> ptr;
        QWeakPointer<const Data> weakref;

        QCOMPARE(sizeof(ptr), 2*sizeof(void*));
        QCOMPARE(sizeof(weakref), 2*sizeof(void*));
    }

    QFETCH(bool, isNull);
    Data *aData = 0;
    if (!isNull)
        aData = new Data;
    Data *otherData = new Data;
    QSharedPointer<Data> ptr(aData);

    {
        // basic self tests
        QCOMPARE(ptr.isNull(), isNull);
        QCOMPARE(bool(ptr), !isNull);
        QCOMPARE(!ptr, isNull);

        QCOMPARE(ptr.data(), aData);
        QCOMPARE(ptr.get(), aData);
        QCOMPARE(ptr.operator->(), aData);
        if (!isNull) {
            Data &dataReference = *ptr;
            QCOMPARE(&dataReference, aData);
        }

        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<Data> 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<Data> 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<Data> weak(ptr);
        QCOMPARE(weak.isNull(), isNull);
        QCOMPARE(!weak, isNull);
        QCOMPARE(bool(weak), !isNull);

        QCOMPARE(weak.isNull(), (weak == nullptr));
        QCOMPARE(weak.isNull(), (nullptr == weak));
        QCOMPARE(!weak.isNull(), (weak != nullptr));
        QCOMPARE(!weak.isNull(), (nullptr != weak));

        QVERIFY(ptr == weak);
        QVERIFY(weak == ptr);
        QVERIFY(! (ptr != weak));
        QVERIFY(! (weak != ptr));

        // create another reference:
        QWeakPointer<Data> weak2(weak);
        QCOMPARE(weak2.isNull(), isNull);
        QCOMPARE(!weak2, isNull);
        QCOMPARE(bool(weak2), !isNull);

        QVERIFY(weak2 == weak);
        QVERIFY(weak == weak2);
        QVERIFY(! (weak2 != weak));
        QVERIFY(! (weak != weak2));

        // create a strong reference back:
        QSharedPointer<Data> strong(weak);
        QVERIFY(strong == weak);
        QVERIFY(strong == ptr);
        QCOMPARE(strong.data(), aData);
        QCOMPARE(strong.get(), aData);
    }
    QVERIFY(!refCountData(ptr) || refCountData(ptr)->weakref.loadRelaxed() == 1);
    QVERIFY(!refCountData(ptr) || refCountData(ptr)->strongref.loadRelaxed() == 1);

    // aData is deleted here
}

void tst_QSharedPointer::operators()
{
    QSharedPointer<char> p1;
    QSharedPointer<char> p2(new char);
    qptrdiff diff = p2.data() - p1.data();
    QVERIFY(p1.data() != p2.data());
    QVERIFY(p1.get() != p2.get());
    QVERIFY(diff != 0);

    // operator-
    QCOMPARE(p2 - p1.data(), diff);
    QCOMPARE(p2 - p1.get(), diff);
    QCOMPARE(p2.data() - p1, diff);
    QCOMPARE(p2.get() - p1, diff);
    QCOMPARE(p2 - p1, diff);
    QCOMPARE(p1 - p2, -diff);
    QCOMPARE(p1 - p1, qptrdiff(0));
    QCOMPARE(p2 - p2, qptrdiff(0));

    // operator<
    QVERIFY(p1 < p2.data());
    QVERIFY(p1 < p2.get());
    QVERIFY(p1.data() < p2);
    QVERIFY(p1.get() < p2);
    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<char> p1(nullptr);
    QSharedPointer<char> p2 = nullptr;
    QSharedPointer<char> 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);

    QWeakPointer<char> wp1 = p1;
    QVERIFY(wp1 == nullptr);
    QVERIFY(nullptr == wp1);
    QCOMPARE(wp1, nullptr);
    QCOMPARE(nullptr, wp1);

    QSharedPointer<char> 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<char> p1_zero(0);
    QSharedPointer<char> p2_zero = 0;

    p3 = 0;

    QSharedPointer<char> 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);

    QWeakPointer<char> wp2 = p4;
    QVERIFY(wp2 != nullptr);
    QVERIFY(nullptr != wp2);
}

void tst_QSharedPointer::swap()
{
    QSharedPointer<int> p1, p2(new int(42)), control = p2;
    QVERIFY(p1 != control);
    QVERIFY(p1.isNull());
    QVERIFY(p2 == control);
    QVERIFY(!p2.isNull());
    QVERIFY(*p2 == 42);

    p1.swap(p2);
    QVERIFY(p1 == control);
    QVERIFY(!p1.isNull());
    QVERIFY(p2 != control);
    QVERIFY(p2.isNull());
    QVERIFY(*p1 == 42);

    p1.swap(p2);
    QVERIFY(p1 != control);
    QVERIFY(p1.isNull());
    QVERIFY(p2 == control);
    QVERIFY(!p2.isNull());
    QVERIFY(*p2 == 42);

    qSwap(p1, p2);
    QVERIFY(p1 == control);
    QVERIFY(!p1.isNull());
    QVERIFY(p2 != control);
    QVERIFY(p2.isNull());
    QVERIFY(*p1 == 42);

    QWeakPointer<int> w1, w2 = control;

    QVERIFY(w1.isNull());
    QVERIFY(!w2.isNull());
    QVERIFY(w2.lock() == control);
    QVERIFY(!w1.lock());

    w1.swap(w2);
    QVERIFY(w2.isNull());
    QVERIFY(!w1.isNull());
    QVERIFY(w1.lock() == control);
    QVERIFY(!w2.lock());

    qSwap(w1, w2);
    QVERIFY(w1.isNull());
    QVERIFY(w2.lock() == control);

    p1.reset();
    p2.reset();
    control.reset();

    QVERIFY(w1.isNull());
    QVERIFY(w2.isNull());
}

void tst_QSharedPointer::moveSemantics()
{
    QSharedPointer<int> p1, p2(new int(42)), control = p2;
    QVERIFY(p1 != control);
    QVERIFY(p1.isNull());
    QVERIFY(p2 == control);
    QVERIFY(!p2.isNull());
    QVERIFY(*p2 == 42);

    // move assignment
    p1 = std::move(p2);
    QVERIFY(p1 == control);
    QVERIFY(!p1.isNull());
    QVERIFY(p2 != control);
    QVERIFY(p2.isNull());
    QVERIFY(*p1 == 42);

    // move construction
    QSharedPointer<int> p3 = std::move(p1);
    QVERIFY(p1 != control);
    QVERIFY(p1.isNull());
    QVERIFY(p3 == control);
    QVERIFY(!p3.isNull());
    QVERIFY(*p3 == 42);

    QWeakPointer<int> w1, w2 = control;

    QVERIFY(w1.isNull());
    QVERIFY(!w2.isNull());
    QVERIFY(w2.lock() == control);
    QVERIFY(!w1.lock());

    // move assignment
    w1 = std::move(w2);
    QVERIFY(w2.isNull());
    QVERIFY(!w1.isNull());
    QVERIFY(w1.lock() == control);
    QVERIFY(!w2.lock());

    // move construction
    QWeakPointer<int> w3 = std::move(w1);
    QVERIFY(w1.isNull());
    QVERIFY(!w3.isNull());
    QVERIFY(w3.lock() == control);
    QVERIFY(!w1.lock());

    p1.reset();
    p2.reset();
    p3.reset();
    control.reset();

    QVERIFY(w1.isNull());
    QVERIFY(w2.isNull());
    QVERIFY(w3.isNull());
}

void tst_QSharedPointer::useOfForwardDeclared()
{
    // this just a compile test: use the forward-declared class
    QSharedPointer<ForwardDeclared> sp;

    // copying should work, too:
    QSharedPointer<ForwardDeclared> sp2 = sp;

    // and assignment:
    QSharedPointer<ForwardDeclared> sp3;
    sp3 = sp;

    // move assignment:
    QSharedPointer<ForwardDeclared> sp4;
    sp4 = std::move(sp);

    // and move constuction:
    QSharedPointer<ForwardDeclared> sp5 = std::move(sp2);

    // swapping:
    sp4.swap(sp3);
    qSwap(sp4, sp3);

    // and, of course, destruction
}


void tst_QSharedPointer::memoryManagement()
{
QT_WARNING_PUSH
QT_WARNING_DISABLE_CLANG("-Wself-assign-overloaded")

    int generation = Data::generationCounter + 1;
    int destructorCounter = Data::destructorCounter;

    QSharedPointer<Data> ptr = QSharedPointer<Data>(new Data);
    QCOMPARE(ptr->generation, generation);
    QCOMPARE(Data::destructorCounter, destructorCounter);
    QCOMPARE(Data::generationCounter, generation);

    ptr = ptr;
    QCOMPARE(ptr->generation, generation);
    QCOMPARE(Data::destructorCounter, destructorCounter);
    QCOMPARE(Data::generationCounter, generation);

    {
        QSharedPointer<Data> copy = ptr;
        QCOMPARE(ptr->generation, generation);
        QCOMPARE(copy->generation, generation);

        // copy goes out of scope, ptr continues
    }
    QCOMPARE(ptr->generation, generation);
    QCOMPARE(Data::destructorCounter, destructorCounter);
    QCOMPARE(Data::generationCounter, generation);

    {
        QWeakPointer<Data> weak = ptr;
        weak = ptr;
        QCOMPARE(ptr->generation, generation);
        QCOMPARE(Data::destructorCounter, destructorCounter);
        QCOMPARE(Data::generationCounter, generation);

        weak = weak;
        QCOMPARE(ptr->generation, generation);
        QCOMPARE(Data::destructorCounter, destructorCounter);
        QCOMPARE(Data::generationCounter, generation);

        QSharedPointer<Data> strong = weak;
        QCOMPARE(ptr->generation, generation);
        QCOMPARE(strong->generation, generation);
        QCOMPARE(Data::destructorCounter, destructorCounter);
        QCOMPARE(Data::generationCounter, generation);

        // both weak and strong go out of scope
    }
    QCOMPARE(ptr->generation, generation);
    QCOMPARE(Data::destructorCounter, destructorCounter);
    QCOMPARE(Data::generationCounter, generation);

    QWeakPointer<Data> weak = ptr;
    ptr = QSharedPointer<Data>();

    // destructor must have been called
    QCOMPARE(Data::destructorCounter, destructorCounter + 1);
    QVERIFY(ptr.isNull());
    QVERIFY(weak.isNull());

    // if we create a strong pointer from the weak, it must still be null
    ptr = weak;
    QVERIFY(ptr.isNull());
    QVERIFY(ptr == 0);
    QCOMPARE(ptr.data(), (Data*)0);
QT_WARNING_POP
}

void tst_QSharedPointer::dropLastReferenceOfForwardDeclared()
{
    // pointer to shared-pointer is weird, but we need to do it so that
    // we can drop the last reference in a different .cpp than where it was created
    forwardDeclaredDestructorRunCount = 0;
    delete forwardPointer();
    QCOMPARE(forwardDeclaredDestructorRunCount, 1);
}

// NVD for "non-virtual destructor"
struct NVDData
{
    static int destructorCounter;
    ~NVDData() { ++destructorCounter; }

    int dummy;
};
int NVDData::destructorCounter;

struct NVDDerivedData : NVDData
{
    static int destructorCounter;
    ~NVDDerivedData() { ++destructorCounter; }
};
int NVDDerivedData::destructorCounter;

void tst_QSharedPointer::nonVirtualDestructors()
{
    NVDData::destructorCounter = NVDDerivedData::destructorCounter = 0;
    {
        QSharedPointer<NVDData> ptr(new NVDData);
    }
    QCOMPARE(NVDData::destructorCounter, 1);
    QCOMPARE(NVDDerivedData::destructorCounter, 0);

    NVDData::destructorCounter = NVDDerivedData::destructorCounter = 0;
    {
        QSharedPointer<NVDDerivedData> ptr(new NVDDerivedData);
    }
    QCOMPARE(NVDData::destructorCounter, 1);
    QCOMPARE(NVDDerivedData::destructorCounter, 1);

    NVDData::destructorCounter = NVDDerivedData::destructorCounter = 0;
    {
        QSharedPointer<NVDData> bptr;
        QSharedPointer<NVDDerivedData> ptr(new NVDDerivedData);
        bptr = ptr;
    }
    QCOMPARE(NVDData::destructorCounter, 1);
    QCOMPARE(NVDDerivedData::destructorCounter, 1);

    NVDData::destructorCounter = NVDDerivedData::destructorCounter = 0;
    {
        QSharedPointer<NVDData> ptr(new NVDDerivedData);
    }
    QCOMPARE(NVDData::destructorCounter, 1);
    QCOMPARE(NVDDerivedData::destructorCounter, 1);
}

void tst_QSharedPointer::lock()
{
    QSharedPointer<int> sp = QSharedPointer<int>::create();
    QVERIFY(sp);
    QWeakPointer<int> wp = sp;
    QVERIFY(sp == wp);
    QVERIFY(sp == wp.lock());
    QVERIFY(sp == wp.toStrongRef());

    sp.reset();
    QVERIFY(!wp);
    QVERIFY(sp != wp); // this is why op(shared_ptr, weak_ptr) is a bad idea (apart from MT races)...
    QVERIFY(sp == wp.lock());
    QVERIFY(sp == wp.toStrongRef());
}

class DerivedData: public Data
{
public:
    static int derivedDestructorCounter;
    int moreData;
    DerivedData() : moreData(0) { }
    ~DerivedData() { ++derivedDestructorCounter; }

    void virtualDelete() override
    {
        delete this;
    }

    int classLevel() override { return 2; }
};
int DerivedData::derivedDestructorCounter = 0;

class Stuffing
{
public:
    char buffer[16];
    Stuffing() { for (uint i = 0; i < sizeof buffer; ++i) buffer[i] = 16 - i; }
    virtual ~Stuffing() { }
};

class DiffPtrDerivedData: public Stuffing, public Data
{
public:
    int classLevel() override { return 3; }
};

class VirtualDerived: virtual public Data
{
public:
    int moreData;

    VirtualDerived() : moreData(0xc0ffee) { }
    int classLevel() override { return 4; }
};

void tst_QSharedPointer::downCast()
{
    {
        // copy construction
        QSharedPointer<DerivedData> ptr = QSharedPointer<DerivedData>(new DerivedData);
        QSharedPointer<DerivedData> copy = ptr;
        QSharedPointer<Data> baseptr = qSharedPointerCast<Data>(ptr);
        QSharedPointer<Data> other;
        QWeakPointer<DerivedData> weak = ptr;
        QWeakPointer<Data> baseweak = qSharedPointerCast<Data>(ptr);
        QWeakPointer<Data> baseweak2 = qSharedPointerCast<Data>(weak);

        QVERIFY(ptr == baseptr);
        QVERIFY(baseptr == ptr);
        QVERIFY(! (ptr != baseptr));
        QVERIFY(! (baseptr != ptr));

        QVERIFY(ptr != other);
        QVERIFY(other != ptr);
        QVERIFY(! (ptr == other));
        QVERIFY(! (other == ptr));

        // copy assignments
        baseptr = qSharedPointerCast<Data>(ptr);
        baseweak = qSharedPointerCast<Data>(ptr);
        baseweak2 = baseweak;

        // move assignments (these don't actually move)
        baseptr = qSharedPointerCast<Data>(std::move(ptr));
        ptr = copy;
        baseweak = qSharedPointerCast<Data>(std::move(ptr));
        ptr = copy;
        baseweak2 = qSharedPointerCast<Data>(std::move(baseweak));

        // move construction (these don't actually move)
        ptr = copy;
        QSharedPointer<Data> ptr3(qSharedPointerCast<Data>(std::move(ptr)));
        ptr = copy;
        QWeakPointer<Data> baseweak3(qSharedPointerCast<Data>(std::move(ptr)));
        ptr = copy;
        QWeakPointer<Data> baseweak4(qSharedPointerCast<Data>(std::move(weak)));
    }

    {
        // copy construction
        QSharedPointer<DerivedData> ptr = QSharedPointer<DerivedData>(new DerivedData);
        QSharedPointer<DerivedData> copy = ptr;
        QSharedPointer<Data> baseptr = ptr;
        QWeakPointer<DerivedData> weak = ptr;
        QWeakPointer<Data> baseweak = ptr;
        QWeakPointer<Data> baseweak2 = weak;

        // copy assignments
        baseptr = ptr;
        baseweak = ptr;
        baseweak2 = weak;

        // move assignments (only the QSharedPointer-QSharedPointer actually moves)
        baseweak = std::move(ptr);
        baseweak2 = std::move(weak);
        ptr = copy;
        baseptr = std::move(ptr);

        // move construction (only the QSharedPointer-QSharedPointer actually moves)
        ptr = copy;
        QWeakPointer<Data> baseweak3(std::move(ptr));
        ptr = copy;
        QWeakPointer<Data> baseweak4(std::move(weak));
        ptr = copy;
        QSharedPointer<Data> baseptr2(std::move(ptr));
    }

    int destructorCount;
    destructorCount = DerivedData::derivedDestructorCounter;
    {
        QSharedPointer<Data> baseptr;
        {
            QSharedPointer<DerivedData> ptr = QSharedPointer<DerivedData>(new DerivedData);
            baseptr = ptr;
            QVERIFY(baseptr == ptr);
        }
    }
    QCOMPARE(DerivedData::derivedDestructorCounter, destructorCount + 1);

    destructorCount = DerivedData::derivedDestructorCounter;
    {
        QSharedPointer<DerivedData> ptr = QSharedPointer<DerivedData>(new DerivedData);
        QWeakPointer<Data> baseptr = ptr;
        QVERIFY(baseptr == ptr);

        ptr = QSharedPointer<DerivedData>();
        QVERIFY(baseptr.isNull());
    }
    QCOMPARE(DerivedData::derivedDestructorCounter, destructorCount + 1);

    destructorCount = DerivedData::derivedDestructorCounter;
    {
        QSharedPointer<DerivedData> ptr = QSharedPointer<DerivedData>(new DerivedData);
        QWeakPointer<DerivedData> weakptr(ptr);

        QSharedPointer<Data> baseptr = weakptr;
        QVERIFY(baseptr == ptr);
        QWeakPointer<Data> baseweakptr = weakptr;
        QVERIFY(baseweakptr == ptr);
    }
    QCOMPARE(DerivedData::derivedDestructorCounter, destructorCount + 1);
}

void functionDataByValue(QSharedPointer<Data> p) { Q_UNUSED(p); };
void functionDataByRef(const QSharedPointer<Data> &p) { Q_UNUSED(p); };
void tst_QSharedPointer::functionCallDownCast()
{
    QSharedPointer<DerivedData> p(new DerivedData());
    functionDataByValue(p);
    functionDataByRef(p);
}

void tst_QSharedPointer::upCast()
{
    QSharedPointer<Data> baseptr = QSharedPointer<Data>(new DerivedData);

    {
        QSharedPointer<DerivedData> derivedptr = qSharedPointerCast<DerivedData>(baseptr);
        QVERIFY(baseptr == derivedptr);
        QCOMPARE(static_cast<Data *>(derivedptr.data()), baseptr.data());
    }
    QCOMPARE(int(refCountData(baseptr)->weakref.loadRelaxed()), 1);
    QCOMPARE(int(refCountData(baseptr)->strongref.loadRelaxed()), 1);

    {
        QWeakPointer<DerivedData> derivedptr = qWeakPointerCast<DerivedData>(baseptr);
        QVERIFY(baseptr == derivedptr);
    }
    QCOMPARE(int(refCountData(baseptr)->weakref.loadRelaxed()), 1);
    QCOMPARE(int(refCountData(baseptr)->strongref.loadRelaxed()), 1);

    {
        QWeakPointer<Data> weakptr = baseptr;
        QSharedPointer<DerivedData> derivedptr = qSharedPointerCast<DerivedData>(weakptr);
        QVERIFY(baseptr == derivedptr);
        QCOMPARE(static_cast<Data *>(derivedptr.data()), baseptr.data());
    }
    QCOMPARE(int(refCountData(baseptr)->weakref.loadRelaxed()), 1);
    QCOMPARE(int(refCountData(baseptr)->strongref.loadRelaxed()), 1);

    {
        QSharedPointer<DerivedData> derivedptr = baseptr.staticCast<DerivedData>();
        QVERIFY(baseptr == derivedptr);
        QCOMPARE(static_cast<Data *>(derivedptr.data()), baseptr.data());
    }
    QCOMPARE(int(refCountData(baseptr)->weakref.loadRelaxed()), 1);
    QCOMPARE(int(refCountData(baseptr)->strongref.loadRelaxed()), 1);
}

class OtherObject: public QObject
{
    Q_OBJECT
};

void tst_QSharedPointer::qobjectWeakManagement()
{
    {
        QWeakPointer<QObject> weak;
        weak = QWeakPointer<QObject>();
        QVERIFY(weak.isNull());
        QVERIFY(weak.toStrongRef().isNull());
    }

    {
        QObject *obj = new QObject;
        QSharedPointer<QObject> shared(obj);
        QWeakPointer<QObject> weak(shared);
        QVERIFY(!weak.isNull());
        QVERIFY(weak.toStrongRef() == obj);

        // now delete
        shared.reset();
        QVERIFY(weak.isNull());
    }
    safetyCheck();

    {
        // same, bit with operator=
        QObject *obj = new QObject;
        QSharedPointer<QObject> shared(obj);
        QWeakPointer<QObject> weak;
        weak = shared;
        QVERIFY(!weak.isNull());
        QVERIFY(weak.toStrongRef() == obj);

        // now delete
        shared.reset();
        QVERIFY(weak.isNull());
    }
    safetyCheck();

    {
        // with two QWeakPointers
        QObject *obj = new QObject;
        QSharedPointer<QObject> shared(obj);
        QWeakPointer<QObject> weak(shared);

        {
            QWeakPointer<QObject> weak2(shared);
            QVERIFY(!weak2.isNull());
            QVERIFY(weak == weak2);
        }
        QVERIFY(!weak.isNull());

        shared.reset();
        QVERIFY(weak.isNull());
    }
    safetyCheck();

    {
        // same, but delete the pointer while two QWeakPointers exist
        QObject *obj = new QObject;
        QSharedPointer<QObject> shared(obj);
        QWeakPointer<QObject> weak(shared);

        {
            QWeakPointer<QObject> weak2(shared);
            QVERIFY(!weak2.isNull());

            shared.reset();
            QVERIFY(weak.isNull());
            QVERIFY(weak2.isNull());
        }
        QVERIFY(weak.isNull());
    }
    safetyCheck();
}

void tst_QSharedPointer::weakQObjectFromSharedPointer()
{
    {
        QSharedPointer<QObject> shared(new QObject);
        QWeakPointer<QObject> weak = shared;
        QVERIFY(!weak.isNull());

        // delete:
        shared.clear();
        QVERIFY(weak.isNull());
    }
}

void tst_QSharedPointer::objectCast()
{
    {
        OtherObject *data = new OtherObject;
        QSharedPointer<QObject> baseptr = QSharedPointer<QObject>(data);
        QVERIFY(baseptr == data);
        QVERIFY(data == baseptr);

        // perform object cast
        QSharedPointer<OtherObject> ptr = qSharedPointerObjectCast<OtherObject>(baseptr);
        QVERIFY(!ptr.isNull());
        QCOMPARE(ptr.data(), data);
        QVERIFY(ptr == data);

        // again:
        ptr = baseptr.objectCast<OtherObject>();
        QVERIFY(ptr == data);

        // again:
        ptr = qobject_cast<OtherObject *>(baseptr);
        QVERIFY(ptr == data);

        // again:
        ptr = qobject_cast<QSharedPointer<OtherObject> >(baseptr);
        QVERIFY(ptr == data);
    }
    safetyCheck();

    {
        const OtherObject *data = new OtherObject;
        QSharedPointer<const QObject> baseptr = QSharedPointer<const QObject>(data);
        QVERIFY(baseptr == data);
        QVERIFY(data == baseptr);

        // perform object cast
        QSharedPointer<const OtherObject> ptr = qSharedPointerObjectCast<const OtherObject>(baseptr);
        QVERIFY(!ptr.isNull());
        QCOMPARE(ptr.data(), data);
        QVERIFY(ptr == data);

        // again:
        ptr = baseptr.objectCast<const OtherObject>();
        QVERIFY(ptr == data);

        // again:
        ptr = qobject_cast<const OtherObject *>(baseptr);
        QVERIFY(ptr == data);

        // again:
        ptr = qobject_cast<QSharedPointer<const OtherObject> >(baseptr);
        QVERIFY(ptr == data);
    }
    safetyCheck();

    {
        OtherObject *data = new OtherObject;
        QSharedPointer<OtherObject> ptr = QSharedPointer<OtherObject>(data);
        QWeakPointer<QObject> weakptr = ptr;

        {
            // perform object cast
            QSharedPointer<OtherObject> otherptr = qSharedPointerObjectCast<OtherObject>(weakptr);
            QVERIFY(otherptr == ptr);

            // again:
            otherptr = qobject_cast<OtherObject *>(weakptr);
            QVERIFY(otherptr == ptr);

            // again:
            otherptr = qobject_cast<QSharedPointer<OtherObject> >(weakptr);
            QVERIFY(otherptr == ptr);
        }

        // drop the reference:
        ptr.clear();
        QVERIFY(ptr.isNull());
        QVERIFY(weakptr.toStrongRef().isNull());
        QVERIFY(weakptr.lock().isNull());

        // verify that the object casts fail without crash
        QSharedPointer<OtherObject> otherptr = qSharedPointerObjectCast<OtherObject>(weakptr);
        QVERIFY(otherptr.isNull());

        // again:
        otherptr = qobject_cast<OtherObject *>(weakptr);
        QVERIFY(otherptr.isNull());

        // again:
        otherptr = qobject_cast<QSharedPointer<OtherObject> >(weakptr);
        QVERIFY(otherptr.isNull());
    }
    safetyCheck();
}


void tst_QSharedPointer::objectCastStdSharedPtr()
{
    {
        OtherObject *data = new OtherObject;
        std::shared_ptr<QObject> baseptr = std::shared_ptr<QObject>(data);
        QVERIFY(baseptr.get() == data);

        // perform successful object cast
        std::shared_ptr<OtherObject> ptr = qobject_pointer_cast<OtherObject>(baseptr);
        QVERIFY(ptr.get());
        QVERIFY(ptr.get() == data);

        QVERIFY(baseptr.get() == data);
    }

    {
        OtherObject *data = new OtherObject;
        std::shared_ptr<QObject> baseptr = std::shared_ptr<QObject>(data);
        QVERIFY(baseptr.get() == data);

        // perform successful object cast
        std::shared_ptr<OtherObject> ptr = qobject_pointer_cast<OtherObject>(std::move(baseptr));
        QVERIFY(ptr.get());
        QVERIFY(ptr.get() == data);

        QVERIFY(!baseptr.get());
    }

    {
        QObject *data = new QObject;
        std::shared_ptr<QObject> baseptr = std::shared_ptr<QObject>(data);
        QVERIFY(baseptr.get() == data);

        // perform unsuccessful object cast
        std::shared_ptr<OtherObject> ptr = qobject_pointer_cast<OtherObject>(baseptr);
        QVERIFY(!ptr.get());

        QVERIFY(baseptr.get() == data);
    }

    {
        QObject *data = new QObject;
        std::shared_ptr<QObject> baseptr = std::shared_ptr<QObject>(data);
        QVERIFY(baseptr.get() == data);

        // perform unsuccessful object cast
        std::shared_ptr<OtherObject> ptr = qobject_pointer_cast<OtherObject>(std::move(baseptr));
        QVERIFY(!ptr.get());

        QVERIFY(baseptr.get() == data);
    }
}

void tst_QSharedPointer::differentPointers()
{
    {
        DiffPtrDerivedData *aData = new DiffPtrDerivedData;
        Data *aBase = aData;

        // ensure that this compiler isn't broken
        if (*reinterpret_cast<quintptr *>(&aData) == *reinterpret_cast<quintptr *>(&aBase))
            QFAIL("Something went very wrong -- we couldn't create two different pointers to the same object");
        if (aData != aBase || aBase != aData)
            QSKIP("Broken compiler");

        QSharedPointer<DiffPtrDerivedData> ptr = QSharedPointer<DiffPtrDerivedData>(aData);
        QSharedPointer<Data> baseptr = qSharedPointerCast<Data>(ptr);
        qDebug("naked: orig: %p; base: %p (%s) -- QSharedPointer: orig: %p; base %p (%s) -- result: %s",
               aData, aBase, aData == aBase ? "equal" : "not equal",
               ptr.data(), baseptr.data(), ptr.data() == baseptr.data() ? "equal" : "not equal",
               baseptr.data() == aData ? "equal" : "not equal");

        QVERIFY(ptr.data() == baseptr.data());
        QVERIFY(baseptr.data() == ptr.data());
        QVERIFY(ptr == baseptr);
        QVERIFY(baseptr == ptr);

        QVERIFY(ptr.data() == aBase);
        QVERIFY(aBase == ptr.data());
        QVERIFY(ptr.data() == aData);
        QVERIFY(aData == ptr.data());

        QVERIFY(ptr == aBase);
        QVERIFY(aBase == ptr);
        QVERIFY(ptr == aData);
        QVERIFY(aData == ptr);

        QVERIFY(baseptr.data() == aBase);
        QVERIFY(aBase == baseptr.data());
        QVERIFY(baseptr == aBase);
        QVERIFY(aBase == baseptr);

        QVERIFY(baseptr.data() == aData);
        QVERIFY(aData == baseptr.data());
        QVERIFY(baseptr == aData);
        QVERIFY(aData == baseptr);
    }
    safetyCheck();

    {
        DiffPtrDerivedData *aData = new DiffPtrDerivedData;
        Data *aBase = aData;
        QVERIFY(aData == aBase);
        QVERIFY(*reinterpret_cast<quintptr *>(&aData) != *reinterpret_cast<quintptr *>(&aBase));

        QSharedPointer<Data> baseptr = QSharedPointer<Data>(aData);
        QSharedPointer<DiffPtrDerivedData> ptr = qSharedPointerCast<DiffPtrDerivedData>(baseptr);
        QVERIFY(ptr == baseptr);
        QVERIFY(ptr.data() == baseptr.data());
        QVERIFY(ptr == aBase);
        QVERIFY(baseptr == aData);
    }
    safetyCheck();

    {
        DiffPtrDerivedData *aData = new DiffPtrDerivedData;
        Data *aBase = aData;
        QVERIFY(aData == aBase);
        QVERIFY(*reinterpret_cast<quintptr *>(&aData) != *reinterpret_cast<quintptr *>(&aBase));

        QSharedPointer<DiffPtrDerivedData> ptr = QSharedPointer<DiffPtrDerivedData>(aData);
        QSharedPointer<Data> baseptr = ptr;
        QVERIFY(ptr == baseptr);
        QVERIFY(ptr.data() == baseptr.data());
        QVERIFY(ptr == aBase);
        QVERIFY(ptr == aData);
        QVERIFY(baseptr == aData);
        QVERIFY(baseptr == aBase);
    }
    safetyCheck();
}

void tst_QSharedPointer::virtualBaseDifferentPointers()
{
    {
        VirtualDerived *aData = new VirtualDerived;
        Data *aBase = aData;
        QVERIFY(aData == aBase);
        QVERIFY(*reinterpret_cast<quintptr *>(&aData) != *reinterpret_cast<quintptr *>(&aBase));

        QSharedPointer<VirtualDerived> ptr = QSharedPointer<VirtualDerived>(aData);
        QSharedPointer<Data> baseptr = qSharedPointerCast<Data>(ptr);
        QVERIFY(ptr == baseptr);
        QVERIFY(ptr.data() == baseptr.data());
        QVERIFY(ptr == aBase);
        QVERIFY(ptr == aData);
        QVERIFY(baseptr == aData);
        QVERIFY(baseptr == aBase);
    }
    safetyCheck();

    {
        VirtualDerived *aData = new VirtualDerived;
        Data *aBase = aData;
        QVERIFY(aData == aBase);
        QVERIFY(*reinterpret_cast<quintptr *>(&aData) != *reinterpret_cast<quintptr *>(&aBase));

        QSharedPointer<VirtualDerived> ptr = QSharedPointer<VirtualDerived>(aData);
        QSharedPointer<Data> baseptr = ptr;
        QVERIFY(ptr == baseptr);
        QVERIFY(ptr.data() == baseptr.data());
        QVERIFY(ptr == aBase);
        QVERIFY(ptr == aData);
        QVERIFY(baseptr == aData);
        QVERIFY(baseptr == aBase);
    }
    safetyCheck();
    {
        VirtualDerived *aData = new VirtualDerived;

        QSharedPointer<VirtualDerived> ptr = QSharedPointer<VirtualDerived>(aData);
        QWeakPointer<VirtualDerived> wptr = ptr;

        ptr.reset();
        QVERIFY(wptr.toStrongRef().isNull());

        QWeakPointer<Data> wptr2 = wptr;
        QVERIFY(wptr2.toStrongRef().isNull());

        QWeakPointer<Data> wptr3 = std::move(wptr);
        QVERIFY(wptr3.toStrongRef().isNull());
    }
    safetyCheck();
}

#ifndef QTEST_NO_RTTI
void tst_QSharedPointer::dynamicCast()
{
    DerivedData *aData = new DerivedData;
    QSharedPointer<Data> baseptr = QSharedPointer<Data>(aData);

    {
        QSharedPointer<DerivedData> derivedptr = qSharedPointerDynamicCast<DerivedData>(baseptr);
        QVERIFY(baseptr == derivedptr);
        QCOMPARE(derivedptr.data(), aData);
        QCOMPARE(static_cast<Data *>(derivedptr.data()), baseptr.data());
    }
    QCOMPARE(int(refCountData(baseptr)->weakref.loadRelaxed()), 1);
    QCOMPARE(int(refCountData(baseptr)->strongref.loadRelaxed()), 1);

    {
        QWeakPointer<Data> weakptr = baseptr;
        QSharedPointer<DerivedData> derivedptr = qSharedPointerDynamicCast<DerivedData>(weakptr);
        QVERIFY(baseptr == derivedptr);
        QCOMPARE(derivedptr.data(), aData);
        QCOMPARE(static_cast<Data *>(derivedptr.data()), baseptr.data());
    }
    QCOMPARE(int(refCountData(baseptr)->weakref.loadRelaxed()), 1);
    QCOMPARE(int(refCountData(baseptr)->strongref.loadRelaxed()), 1);

    {
        QSharedPointer<DerivedData> derivedptr = baseptr.dynamicCast<DerivedData>();
        QVERIFY(baseptr == derivedptr);
        QCOMPARE(derivedptr.data(), aData);
        QCOMPARE(static_cast<Data *>(derivedptr.data()), baseptr.data());
    }
    QCOMPARE(int(refCountData(baseptr)->weakref.loadRelaxed()), 1);
    QCOMPARE(int(refCountData(baseptr)->strongref.loadRelaxed()), 1);
}

void tst_QSharedPointer::dynamicCastDifferentPointers()
{
    // DiffPtrDerivedData derives from both Data and Stuffing
    DiffPtrDerivedData *aData = new DiffPtrDerivedData;
    QSharedPointer<Data> baseptr = QSharedPointer<Data>(aData);

    {
        QSharedPointer<DiffPtrDerivedData> derivedptr = qSharedPointerDynamicCast<DiffPtrDerivedData>(baseptr);
        QVERIFY(baseptr == derivedptr);
        QCOMPARE(derivedptr.data(), aData);
        QCOMPARE(static_cast<Data *>(derivedptr.data()), baseptr.data());
    }
    QCOMPARE(int(refCountData(baseptr)->weakref.loadRelaxed()), 1);
    QCOMPARE(int(refCountData(baseptr)->strongref.loadRelaxed()), 1);

    {
        QWeakPointer<Data> weakptr = baseptr;
        QSharedPointer<DiffPtrDerivedData> derivedptr = qSharedPointerDynamicCast<DiffPtrDerivedData>(weakptr);
        QVERIFY(baseptr == derivedptr);
        QCOMPARE(derivedptr.data(), aData);
        QCOMPARE(static_cast<Data *>(derivedptr.data()), baseptr.data());
    }
    QCOMPARE(int(refCountData(baseptr)->weakref.loadRelaxed()), 1);
    QCOMPARE(int(refCountData(baseptr)->strongref.loadRelaxed()), 1);

    {
        QSharedPointer<DiffPtrDerivedData> derivedptr = baseptr.dynamicCast<DiffPtrDerivedData>();
        QVERIFY(baseptr == derivedptr);
        QCOMPARE(derivedptr.data(), aData);
        QCOMPARE(static_cast<Data *>(derivedptr.data()), baseptr.data());
    }
    QCOMPARE(int(refCountData(baseptr)->weakref.loadRelaxed()), 1);
    QCOMPARE(int(refCountData(baseptr)->strongref.loadRelaxed()), 1);

    {
        Stuffing *nakedptr = dynamic_cast<Stuffing *>(baseptr.data());
        QVERIFY(nakedptr);

        QSharedPointer<Stuffing> otherbaseptr = qSharedPointerDynamicCast<Stuffing>(baseptr);
        QVERIFY(!otherbaseptr.isNull());
        QVERIFY(otherbaseptr == nakedptr);
        QCOMPARE(otherbaseptr.data(), nakedptr);
        QCOMPARE(static_cast<DiffPtrDerivedData*>(otherbaseptr.data()), aData);
    }
}

void tst_QSharedPointer::dynamicCastVirtualBase()
{
    VirtualDerived *aData = new VirtualDerived;
    QSharedPointer<Data> baseptr = QSharedPointer<Data>(aData);

    {
        QSharedPointer<VirtualDerived> derivedptr = qSharedPointerDynamicCast<VirtualDerived>(baseptr);
        QVERIFY(baseptr == derivedptr);
        QCOMPARE(derivedptr.data(), aData);
        QCOMPARE(static_cast<Data *>(derivedptr.data()), baseptr.data());
    }
    QCOMPARE(int(refCountData(baseptr)->weakref.loadRelaxed()), 1);
    QCOMPARE(int(refCountData(baseptr)->strongref.loadRelaxed()), 1);

    {
        QWeakPointer<Data> weakptr = baseptr;
        QSharedPointer<VirtualDerived> derivedptr = qSharedPointerDynamicCast<VirtualDerived>(weakptr);
        QVERIFY(baseptr == derivedptr);
        QCOMPARE(derivedptr.data(), aData);
        QCOMPARE(static_cast<Data *>(derivedptr.data()), baseptr.data());
    }
    QCOMPARE(int(refCountData(baseptr)->weakref.loadRelaxed()), 1);
    QCOMPARE(int(refCountData(baseptr)->strongref.loadRelaxed()), 1);

    {
        QSharedPointer<VirtualDerived> derivedptr = baseptr.dynamicCast<VirtualDerived>();
        QVERIFY(baseptr == derivedptr);
        QCOMPARE(derivedptr.data(), aData);
        QCOMPARE(static_cast<Data *>(derivedptr.data()), baseptr.data());
    }
    QCOMPARE(int(refCountData(baseptr)->weakref.loadRelaxed()), 1);
    QCOMPARE(int(refCountData(baseptr)->strongref.loadRelaxed()), 1);
}

void tst_QSharedPointer::dynamicCastFailure()
{
    QSharedPointer<Data> baseptr = QSharedPointer<Data>(new Data);
    QVERIFY(dynamic_cast<DerivedData *>(baseptr.data()) == 0);

    {
        QSharedPointer<DerivedData> derivedptr = qSharedPointerDynamicCast<DerivedData>(baseptr);
        QVERIFY(derivedptr.isNull());
    }
    QCOMPARE(int(refCountData(baseptr)->weakref.loadRelaxed()), 1);
    QCOMPARE(int(refCountData(baseptr)->strongref.loadRelaxed()), 1);

    {
        QSharedPointer<DerivedData> derivedptr = baseptr.dynamicCast<DerivedData>();
        QVERIFY(derivedptr.isNull());
    }
    QCOMPARE(int(refCountData(baseptr)->weakref.loadRelaxed()), 1);
    QCOMPARE(int(refCountData(baseptr)->strongref.loadRelaxed()), 1);
}

void tst_QSharedPointer::dynamicCastFailureNoLeak()
{
    NonTracked::dynamicCastFailureNoLeak();
}
#endif

void tst_QSharedPointer::constCorrectness()
{
    {
        QSharedPointer<Data> ptr = QSharedPointer<Data>(new Data);
        QSharedPointer<const Data> cptr(ptr);
        QSharedPointer<volatile Data> vptr(ptr);
        cptr = ptr;
        vptr = ptr;

        ptr = qSharedPointerConstCast<Data>(cptr);
        ptr = qSharedPointerConstCast<Data>(vptr);
        ptr = cptr.constCast<Data>();
        ptr = vptr.constCast<Data>();

        QSharedPointer<const volatile Data> cvptr(ptr);
        QSharedPointer<const volatile Data> cvptr2(cptr);
        QSharedPointer<const volatile Data> cvptr3(vptr);
        cvptr = ptr;
        cvptr2 = cptr;
        cvptr3 = vptr;
        ptr = qSharedPointerConstCast<Data>(cvptr);
        ptr = cvptr.constCast<Data>();
    }
    safetyCheck();

    {
        Data *aData = new Data;
        QSharedPointer<Data> ptr = QSharedPointer<Data>(aData);
        const QSharedPointer<Data> cptr = ptr;

        ptr = cptr;
        QSharedPointer<Data> other = qSharedPointerCast<Data>(cptr);

#ifndef QT_NO_DYNAMIC_CAST
        other = qSharedPointerDynamicCast<Data>(cptr);
#endif

        QCOMPARE(cptr.data(), aData);
        QCOMPARE(cptr.operator->(), aData);
    }
    safetyCheck();
}

static int customDeleterFnCallCount;
void customDeleterFn(Data *ptr)
{
    ++customDeleterFnCallCount;
    delete ptr;
}

static int refcount;

template <typename T>
struct CustomDeleter
{
    CustomDeleter() { ++refcount; }
    CustomDeleter(const CustomDeleter &) { ++refcount; }
    ~CustomDeleter() { --refcount; }
    inline void operator()(T *ptr)
    {
        delete ptr;
        ++callCount;
    }
    static int callCount;
};
template<typename T> int CustomDeleter<T>::callCount = 0;

void tst_QSharedPointer::customDeleter()
{
    {
        QSharedPointer<Data> ptr(0, &Data::doDelete);
        QSharedPointer<Data> ptr2(0, &Data::alsoDelete);
        QSharedPointer<Data> ptr3(0, &Data::virtualDelete);
    }
    safetyCheck();
    {
        QSharedPointer<Data> ptr(nullptr, &Data::doDelete);
        QSharedPointer<Data> ptr2(nullptr, &Data::alsoDelete);
        QSharedPointer<Data> ptr3(nullptr, &Data::virtualDelete);
    }
    safetyCheck();
    {
        QSharedPointer<Data> ptr(new Data, &Data::doDelete);
        QSharedPointer<Data> ptr2(new Data, &Data::alsoDelete);
        QSharedPointer<Data> ptr3(new Data, &Data::virtualDelete);
    }
    safetyCheck();
    {
        QSharedPointer<DerivedData> ptr(new DerivedData, &Data::doDelete);
        QSharedPointer<DerivedData> ptr2(new DerivedData, &Data::alsoDelete);
        QSharedPointer<DerivedData> ptr3(new DerivedData, &Data::virtualDelete);
    }
    safetyCheck();

    customDeleterFnCallCount = 0;
    {
        QSharedPointer<Data> ptr = QSharedPointer<Data>(new Data, customDeleterFn);
        ptr.data();
        QCOMPARE(customDeleterFnCallCount, 0);
    }
    QCOMPARE(customDeleterFnCallCount, 1);
    safetyCheck();

    customDeleterFnCallCount = 0;
    {
        QSharedPointer<Data> ptr = QSharedPointer<Data>(new Data, customDeleterFn);
        QCOMPARE(customDeleterFnCallCount, 0);
        ptr.clear();
        QCOMPARE(customDeleterFnCallCount, 1);
    }
    QCOMPARE(customDeleterFnCallCount, 1);
    safetyCheck();

    customDeleterFnCallCount = 0;
    {
        QSharedPointer<Data> ptr = QSharedPointer<Data>(new Data, customDeleterFn);
        QCOMPARE(customDeleterFnCallCount, 0);
        ptr = QSharedPointer<Data>(new Data);
        QCOMPARE(customDeleterFnCallCount, 1);
    }
    QCOMPARE(customDeleterFnCallCount, 1);
    safetyCheck();

    customDeleterFnCallCount = 0;
    {
        QSharedPointer<Data> ptr = QSharedPointer<Data>(new DerivedData, customDeleterFn);
        ptr.data();
        QCOMPARE(customDeleterFnCallCount, 0);
    }
    QCOMPARE(customDeleterFnCallCount, 1);
    safetyCheck();

    customDeleterFnCallCount = 0;
    {
        QSharedPointer<DerivedData> ptr = QSharedPointer<DerivedData>(new DerivedData, customDeleterFn);
        ptr.data();
        QCOMPARE(customDeleterFnCallCount, 0);
    }
    QCOMPARE(customDeleterFnCallCount, 1);
    safetyCheck();

    customDeleterFnCallCount = 0;
    {
        QSharedPointer<Data> other;
        {
            QSharedPointer<Data> ptr = QSharedPointer<Data>(new Data, customDeleterFn);
            other = ptr;
            QCOMPARE(customDeleterFnCallCount, 0);
        }
        QCOMPARE(customDeleterFnCallCount, 0);
    }
    QCOMPARE(customDeleterFnCallCount, 1);
    safetyCheck();

    customDeleterFnCallCount = 0;
    {
        QSharedPointer<Data> other;
        {
            QSharedPointer<DerivedData> ptr = QSharedPointer<DerivedData>(new DerivedData, customDeleterFn);
            other = ptr;
            QCOMPARE(customDeleterFnCallCount, 0);
        }
        QCOMPARE(customDeleterFnCallCount, 0);
    }
    QCOMPARE(customDeleterFnCallCount, 1);
    safetyCheck();

    refcount = 0;
    CustomDeleter<Data> dataDeleter;
    dataDeleter.callCount = 0;
    {
        QSharedPointer<Data> ptr = QSharedPointer<Data>(new Data, dataDeleter);
        ptr.data();
        QCOMPARE(dataDeleter.callCount, 0);
    }
    QCOMPARE(dataDeleter.callCount, 1);
    QCOMPARE(refcount, 1);
    safetyCheck();

    dataDeleter.callCount = 0;
    {
        QSharedPointer<Data> ptr = QSharedPointer<Data>(new Data, dataDeleter);
        QSharedPointer<Data> other = ptr;
        other.clear();
        QCOMPARE(dataDeleter.callCount, 0);
    }
    QCOMPARE(dataDeleter.callCount, 1);
    QCOMPARE(refcount, 1);
    safetyCheck();

    dataDeleter.callCount = 0;
    {
        QSharedPointer<Data> other;
        {
            QSharedPointer<Data> ptr = QSharedPointer<Data>(new Data, dataDeleter);
            other = ptr;
            QCOMPARE(dataDeleter.callCount, 0);
        }
        QCOMPARE(dataDeleter.callCount, 0);
    }
    QCOMPARE(dataDeleter.callCount, 1);
    QCOMPARE(refcount, 1);
    safetyCheck();

    dataDeleter.callCount = 0;
    {
        QSharedPointer<DerivedData> ptr = QSharedPointer<DerivedData>(new DerivedData, dataDeleter);
        ptr.data();
        QCOMPARE(dataDeleter.callCount, 0);
    }
    QCOMPARE(dataDeleter.callCount, 1);
    QCOMPARE(refcount, 1);
    safetyCheck();

    CustomDeleter<DerivedData> derivedDataDeleter;
    derivedDataDeleter.callCount = 0;
    dataDeleter.callCount = 0;
    {
        QSharedPointer<DerivedData> ptr = QSharedPointer<DerivedData>(new DerivedData, derivedDataDeleter);
        ptr.data();
        QCOMPARE(dataDeleter.callCount, 0);
        QCOMPARE(derivedDataDeleter.callCount, 0);
    }
    QCOMPARE(dataDeleter.callCount, 0);
    QCOMPARE(derivedDataDeleter.callCount, 1);
    QCOMPARE(refcount, 2);
    safetyCheck();

    derivedDataDeleter.callCount = 0;
    dataDeleter.callCount = 0;
    {
        QSharedPointer<Data> other;
        {
            QSharedPointer<DerivedData> ptr = QSharedPointer<DerivedData>(new DerivedData, dataDeleter);
            other = ptr;
            QCOMPARE(dataDeleter.callCount, 0);
            QCOMPARE(derivedDataDeleter.callCount, 0);
        }
        QCOMPARE(dataDeleter.callCount, 0);
        QCOMPARE(derivedDataDeleter.callCount, 0);
    }
    QCOMPARE(dataDeleter.callCount, 1);
    QCOMPARE(derivedDataDeleter.callCount, 0);
    QCOMPARE(refcount, 2);
    safetyCheck();

    derivedDataDeleter.callCount = 0;
    dataDeleter.callCount = 0;
    {
        QSharedPointer<Data> other;
        {
            QSharedPointer<DerivedData> ptr = QSharedPointer<DerivedData>(new DerivedData, derivedDataDeleter);
            other = ptr;
            QCOMPARE(dataDeleter.callCount, 0);
            QCOMPARE(derivedDataDeleter.callCount, 0);
        }
        QCOMPARE(dataDeleter.callCount, 0);
        QCOMPARE(derivedDataDeleter.callCount, 0);
    }
    QCOMPARE(dataDeleter.callCount, 0);
    QCOMPARE(derivedDataDeleter.callCount, 1);
    QCOMPARE(refcount, 2);
    safetyCheck();

    CustomDeleter<NVDData> nvdeleter;
    nvdeleter.callCount = 0;
    {
        QSharedPointer<NVDData> ptr(new NVDData, nvdeleter);
    }
    QCOMPARE(nvdeleter.callCount, 1);
    safetyCheck();

    CustomDeleter<NVDDerivedData> nvderiveddeleter;
    nvdeleter.callCount = 0;
    nvderiveddeleter.callCount = 0;
    {
        QSharedPointer<NVDDerivedData> ptr(new NVDDerivedData, nvderiveddeleter);
    }
    QCOMPARE(nvdeleter.callCount, 0);
    QCOMPARE(nvderiveddeleter.callCount, 1);
    safetyCheck();

    nvdeleter.callCount = 0;
    nvderiveddeleter.callCount = 0;
    {
        QSharedPointer<NVDData> ptr(new NVDDerivedData, nvderiveddeleter);
    }
    QCOMPARE(nvdeleter.callCount, 0);
    QCOMPARE(nvderiveddeleter.callCount, 1);
    safetyCheck();

    // a custom deleter with a different pointer parameter
    {
        QSharedPointer<char> ptr(static_cast<char *>(malloc(1)), free);
    }
    safetyCheck();
}

// The compiler needs to be in C++11 mode and to support lambdas
void tst_QSharedPointer::lambdaCustomDeleter()
{
    {
        // stateless, one-argument
        QSharedPointer<Data> ptr(new Data, [](Data *d) { delete d; });
        QSharedPointer<Data> ptr2(new Data, [](Data *d) { d->doDelete(); });
    }
    safetyCheck();

    customDeleterFnCallCount = 0;
    {
        // stateless, one-argument, modifies globals
        QSharedPointer<Data> ptr(new Data, [](Data *d) { ++customDeleterFnCallCount; delete d; });
    }
    safetyCheck();
    QCOMPARE(customDeleterFnCallCount, 1);

    {
        // stateful by ref, one-argument
        int i = 0;
        QSharedPointer<Data> ptr(new Data, [&i](Data *d) { i = 42; delete d; });
        ptr.clear();
        QCOMPARE(i, 42);
    }
    safetyCheck();
}

void tst_QSharedPointer::customDeleterOnNullptr()
{
    Data *null = nullptr;
    int callCount = 0;
    auto deleter = [&callCount](Data *) { ++callCount; };
    {
        QSharedPointer<Data> ptr(null, deleter);
    }
    safetyCheck();
    QCOMPARE(callCount, 1);

    callCount = 0;
    {
        QSharedPointer<Data> ptr(nullptr, deleter);
    }
    safetyCheck();
    QCOMPARE(callCount, 1);

    callCount = 0;
    {
        QSharedPointer<Data> ptr1(null, deleter);
        QSharedPointer<Data> ptr2(nullptr, deleter);
    }
    safetyCheck();
    QCOMPARE(callCount, 2);
}

void customQObjectDeleterFn(QObject *obj)
{
    ++customDeleterFnCallCount;
    delete obj;
}

void tst_QSharedPointer::creating()
{
    Data::generationCounter = Data::destructorCounter = 0;
    {
        QSharedPointer<Data> ptr = QSharedPointer<Data>::create();
        QVERIFY(ptr.data());
        QCOMPARE(Data::generationCounter, 1);
        QCOMPARE(ptr->generation, 1);
        QCOMPARE(Data::destructorCounter, 0);

        QCOMPARE(ptr->classLevel(), 1);

        ptr.clear();
        QCOMPARE(Data::destructorCounter, 1);
    }
    safetyCheck();

    Data::generationCounter = Data::destructorCounter = 0;
    {
        QSharedPointer<Data> ptr = QSharedPointer<Data>::create();
        QWeakPointer<Data> weakptr = ptr;
        QtSharedPointer::ExternalRefCountData *d = refCountData(ptr);

        ptr.clear();
        QVERIFY(ptr.isNull());
        QCOMPARE(Data::destructorCounter, 1);

        // valgrind will complain here if something happened to the pointer
        QVERIFY(d->weakref.loadRelaxed() == 1);
        QVERIFY(d->strongref.loadRelaxed() == 0);
    }
    safetyCheck();

    Data::generationCounter = Data::destructorCounter = 0;
    DerivedData::derivedDestructorCounter = 0;
    {
        QSharedPointer<Data> ptr = QSharedPointer<DerivedData>::create();
        QCOMPARE(ptr->classLevel(), 2);
        QCOMPARE(ptr.staticCast<DerivedData>()->moreData, 0);
        ptr.clear();

        QCOMPARE(Data::destructorCounter, 1);
        QCOMPARE(DerivedData::derivedDestructorCounter, 1);
    }
    safetyCheck();

    {
        QSharedPointer<Data> ptr = QSharedPointer<DiffPtrDerivedData>::create();
        QCOMPARE(ptr->classLevel(), 3);
        QCOMPARE(ptr.staticCast<DiffPtrDerivedData>()->buffer[7]+0, 16-7);
        QCOMPARE(ptr.staticCast<DiffPtrDerivedData>()->buffer[3]+0, 16-3);
        QCOMPARE(ptr.staticCast<DiffPtrDerivedData>()->buffer[0]+0, 16);
    }
    safetyCheck();

    {
        QSharedPointer<VirtualDerived> ptr = QSharedPointer<VirtualDerived>::create();
        QCOMPARE(ptr->classLevel(), 4);
        QCOMPARE(ptr->moreData, 0xc0ffee);

        QSharedPointer<Data> baseptr = ptr;
        QCOMPARE(baseptr->classLevel(), 4);
    }
    safetyCheck();
}

void tst_QSharedPointer::creatingCvQualified()
{
    auto cptr = QSharedPointer<const Data>::create();
    auto vptr = QSharedPointer<volatile Data>::create();
    auto cvptr = QSharedPointer<const volatile Data>::create();
}

void tst_QSharedPointer::creatingVariadic()
{
    int i = 42;

    {
        NoDefaultConstructor1(1); // control check
        QSharedPointer<NoDefaultConstructor1> ptr = QSharedPointer<NoDefaultConstructor1>::create(1);
        QCOMPARE(ptr->i, 1);

        NoDefaultConstructor1(0u); // control check
        ptr = QSharedPointer<NoDefaultConstructor1>::create(0u);
        QCOMPARE(ptr->i, 42);

        NoDefaultConstructor1 x(i); // control check
        ptr = QSharedPointer<NoDefaultConstructor1>::create(i);
        QCOMPARE(ptr->i, i);
    }
    {
        NoDefaultConstructor2((void*)0, 1); // control check
        QSharedPointer<NoDefaultConstructor2> ptr = QSharedPointer<NoDefaultConstructor2>::create((void*)0, 1);
        QCOMPARE(ptr->i, 1);
        QCOMPARE(ptr->ptr, (void*)0);

        int *null = 0;
        NoDefaultConstructor2(null, 2); // control check
        ptr = QSharedPointer<NoDefaultConstructor2>::create(null, 2);
        QCOMPARE(ptr->i, 2);
        QCOMPARE(ptr->ptr, (void*)0);

        NoDefaultConstructor2(nullptr, 3); // control check
        ptr = QSharedPointer<NoDefaultConstructor2>::create(nullptr, 3);
        QCOMPARE(ptr->i, 3);
        QCOMPARE(ptr->ptr, (void*)nullptr);
    }
    {
        NoDefaultConstructorRef1 x(i); // control check
        QSharedPointer<NoDefaultConstructorRef1> ptr = QSharedPointer<NoDefaultConstructorRef1>::create(i);
        QCOMPARE(ptr->i, i);
        QCOMPARE(&ptr->i, &i);
    }
    {
        NoDefaultConstructorRRef1(std::move(i)); // control check
        QSharedPointer<NoDefaultConstructorRRef1> ptr = QSharedPointer<NoDefaultConstructorRRef1>::create(std::move(i));
        QCOMPARE(ptr->i, i);
    }
    {
        NoDefaultConstructorRRef2(std::unique_ptr<int>(new int(1))); // control check
        QSharedPointer<NoDefaultConstructorRRef2> ptr = QSharedPointer<NoDefaultConstructorRRef2>::create(std::unique_ptr<int>(new int(1)));
        QCOMPARE(*ptr->i, 1);

        std::unique_ptr<int> p(new int(i));
        ptr = QSharedPointer<NoDefaultConstructorRRef2>::create(std::move(p));
        QCOMPARE(*ptr->i, i);
    }
    {
        QString text("Hello, World");
        NoDefaultConstructorRef2(text, 1); // control check
        QSharedPointer<NoDefaultConstructorRef2> ptr = QSharedPointer<NoDefaultConstructorRef2>::create(text, 1);
        QCOMPARE(ptr->str, text);
        QCOMPARE(ptr->i, 1);
    }
    {
        QSharedPointer<NoDefaultConstructorConstRef2> ptr;
        NoDefaultConstructorConstRef2(QLatin1String("string"), 1); // control check
        ptr = QSharedPointer<NoDefaultConstructorConstRef2>::create(QLatin1String("string"), 1);
        QCOMPARE(ptr->str, QString("string"));
        QCOMPARE(ptr->i, 1);

        NoDefaultConstructorConstRef2(QByteArray("bytearray")); // control check
        ptr = QSharedPointer<NoDefaultConstructorConstRef2>::create(QByteArray("bytearray"));
        QCOMPARE(ptr->str, QString("bytearray"));
        QCOMPARE(ptr->i, 42);
    }
}

void tst_QSharedPointer::creatingQObject()
{
    {
        QSharedPointer<QObject> ptr = QSharedPointer<QObject>::create();
        QCOMPARE(ptr->metaObject(), &QObject::staticMetaObject);

        QPointer<QObject> qptr = ptr.data();
        ptr.clear();

        QVERIFY(qptr.isNull());
    }
    safetyCheck();

    {
        QSharedPointer<QObject> ptr = QSharedPointer<OtherObject>::create();
        QCOMPARE(ptr->metaObject(), &OtherObject::staticMetaObject);
    }
    safetyCheck();
}

void tst_QSharedPointer::mixTrackingPointerCode()
{
    {
        // pointer created with tracking
        // deleted in code without tracking
        QSharedPointer<int> ptr = QSharedPointer<int>(new int(42));
        Wrapper w(ptr);
        ptr.clear();
    }
    safetyCheck();

    {
        // pointer created without tracking
        // deleted in code with tracking
        Wrapper w = Wrapper::create();
        w.ptr.clear();
    }
}

class ThreadData
{
    QAtomicInt * volatile ptr;
public:
    ThreadData(QAtomicInt *p) : ptr(p) { }
    ~ThreadData() { QtPrivate::volatilePreIncrement(ptr); }
    void ref()
    {
        // if we're called after the destructor, we'll crash
        ptr->ref();
    }
};

class StrongThread: public QThread
{
protected:
    void run() override
    {
        sleep(std::chrono::microseconds{QRandomGenerator::global()->bounded(2000)});
        ptr->ref();
        ptr.clear();
    }
public:
    QSharedPointer<ThreadData> ptr;
};

class WeakThread: public QThread
{
protected:
    void run() override
    {
        sleep(std::chrono::microseconds{QRandomGenerator::global()->bounded(2000)});
        QSharedPointer<ThreadData> ptr = weak;
        if (ptr)
            ptr->ref();
        ptr.clear();
    }
public:
    QWeakPointer<ThreadData> weak;
};

void tst_QSharedPointer::threadStressTest_data()
{
    QTest::addColumn<int>("strongThreadCount");
    QTest::addColumn<int>("weakThreadCount");

    QTest::newRow("0+0") << 0 << 0;
    QTest::newRow("1+0") << 1 << 0;
    QTest::newRow("2+0") << 2 << 0;
    QTest::newRow("10+0") << 10 << 0;

    QTest::newRow("0+1") << 0 << 1;
    QTest::newRow("1+1") << 1 << 1;

    QTest::newRow("2+10") << 2 << 10;

    QTest::newRow("5+10") << 5 << 10;
    QTest::newRow("5+30") << 5 << 30;

    QTest::newRow("100+100") << 100 << 100;
}

void tst_QSharedPointer::threadStressTest()
{
    QFETCH(int, strongThreadCount);
    QFETCH(int, weakThreadCount);

    int guard1[128];
    QAtomicInt counter;
    int guard2[128];

    memset(guard1, 0, sizeof guard1);
    memset(guard2, 0, sizeof guard2);

    for (int r = 0; r < 5; ++r) {
        QList<QThread*> allThreads(6 * qMax(strongThreadCount, weakThreadCount) + 3, 0);
        QSharedPointer<ThreadData> base = QSharedPointer<ThreadData>(new ThreadData(&counter));
        counter.storeRelaxed(0);

        // set the pointers
        for (int i = 0; i < strongThreadCount; ++i) {
            StrongThread *t = new StrongThread;
            t->ptr = base;
            allThreads[2 * i] = t;
        }
        for (int i = 0; i < weakThreadCount; ++i) {
            WeakThread *t = new WeakThread;
            t->weak = base;
            allThreads[6 * i + 3] = t;
        }

        base.clear();

        // start threads
        for (int i = 0; i < allThreads.size(); ++i)
            if (allThreads[i]) allThreads[i]->start();

        // wait for them to finish
        for (int i = 0; i < allThreads.size(); ++i)
            if (allThreads[i]) allThreads[i]->wait();
        qDeleteAll(allThreads);

        // ensure the guards aren't touched
        for (uint i = 0; i < sizeof guard1 / sizeof guard1[0]; ++i)
            QVERIFY(!guard1[i]);
        for (uint i = 0; i < sizeof guard2 / sizeof guard2[0]; ++i)
            QVERIFY(!guard2[i]);

        // verify that the count is the right range
        int minValue = strongThreadCount;
        int maxValue = strongThreadCount + weakThreadCount;
        QVERIFY(counter.loadRelaxed() >= minValue);
        QVERIFY(counter.loadRelaxed() <= maxValue);
    }
}

template<typename Container, bool Ordered, bool Multi>
void hashAndMapTest()
{
    typedef typename Container::key_type Key;
    typedef typename Container::mapped_type Value;

    Container c;
    QVERIFY(c.isEmpty());

    Key k0;
    c.insert(k0, Value(0));
    QVERIFY(!c.isEmpty());

    typename Container::iterator it;
    it = c.find(k0);
    QVERIFY(it != c.end());
    it = c.find(Key());
    QVERIFY(it != c.end());
    it = c.find(Key(0));
    QVERIFY(it != c.end());

    Key k1(new typename Key::value_type(42));
    it = c.find(k1);
    QVERIFY(it == c.end());

    c.insert(k1, Value(42));
    it = c.find(k1);
    QVERIFY(it != c.end());
    QVERIFY(it != c.find(Key()));

    if (Ordered) {
        QVERIFY(k0 < k1);

        it = c.begin();
        QCOMPARE(it.key(), k0);
        QCOMPARE(it.value(), Value(0));

        ++it;
        QCOMPARE(it.key(), k1);
        QCOMPARE(it.value(), Value(42));

        ++it;
        QVERIFY(it == c.end());
    }

    if (Multi) {
        c.insert(k1, Value(47));
        it = c.find(k1);
        QVERIFY(it != c.end());
        QCOMPARE(it.key(), k1);
        ++it;
        QVERIFY(it != c.end());
        QCOMPARE(it.key(), k1);
        ++it;
        if (Ordered)
            QVERIFY(it == c.end());
    }
}

void tst_QSharedPointer::map()
{
    hashAndMapTest<QMap<QSharedPointer<int>, int>, true, false>();
    hashAndMapTest<QMultiMap<QSharedPointer<int>, int>, true, true>();
}

void tst_QSharedPointer::hash()
{
    hashAndMapTest<QHash<QSharedPointer<int>, int>, false, false>();
    hashAndMapTest<QMultiHash<QSharedPointer<int>, int>, false, true>();
}

void tst_QSharedPointer::validConstructs()
{
    {
        Data *aData = new Data;
        QSharedPointer<Data> ptr1 = QSharedPointer<Data>(aData);

QT_WARNING_PUSH
QT_WARNING_DISABLE_CLANG("-Wself-assign-overloaded")
        ptr1 = ptr1;            // valid
QT_WARNING_POP

        QSharedPointer<Data> ptr2(ptr1);

        ptr1 = ptr2;
        ptr2 = ptr1;

        ptr1 = QSharedPointer<Data>();
        ptr1 = ptr2;
    }
}

#if 0
typedef bool (QTest::QExternalTest:: * TestFunction)(const QByteArray &body);
Q_DECLARE_METATYPE(TestFunction)
void tst_QSharedPointer::invalidConstructs_data()
{
    QTest::addColumn<TestFunction>("testFunction");
    QTest::addColumn<QString>("code");
    QTest::newRow("sanity-checking") << &QTest::QExternalTest::tryCompile << "";

    // QSharedPointer<void> is not allowed
    QTest::newRow("void") << &QTest::QExternalTest::tryCompileFail << "QSharedPointer<void> ptr;";

    // implicit initialization
    QTest::newRow("implicit-initialization1")
        << &QTest::QExternalTest::tryCompileFail
        << "QSharedPointer<Data> ptr = new Data;";
    QTest::newRow("implicit-initialization2")
        << &QTest::QExternalTest::tryCompileFail
        << "QSharedPointer<Data> ptr;"
           "ptr = new Data;";
    QTest::newRow("implicit-initialization3")
        << &QTest::QExternalTest::tryCompileFail
        << "QWeakPointer<Data> ptr = new Data;";
    QTest::newRow("implicit-initialization4")
        << &QTest::QExternalTest::tryCompileFail
        << "QWeakPointer<Data> ptr;"
           "ptr = new Data;";

    // use of forward-declared class
    QTest::newRow("creating-forward-declaration")
        << &QTest::QExternalTest::tryCompileFail
        << "QSharedPointer<ForwardDeclared>::create();";

    // upcast without cast operator:
    QTest::newRow("upcast1")
        << &QTest::QExternalTest::tryCompileFail
        << "QSharedPointer<Data> baseptr = QSharedPointer<Data>(new DerivedData);\n"
           "QSharedPointer<DerivedData> ptr(baseptr);";
    QTest::newRow("upcast2")
        << &QTest::QExternalTest::tryCompileFail
        << "QSharedPointer<Data> baseptr = QSharedPointer<Data>(new DerivedData);\n"
           "QSharedPointer<DerivedData> ptr;\n"
           "ptr = baseptr;";

    // dropping of const
    QTest::newRow("const-dropping1")
        << &QTest::QExternalTest::tryCompileFail
        << "QSharedPointer<const Data> baseptr = QSharedPointer<const Data>(new Data);\n"
           "QSharedPointer<Data> ptr(baseptr);";
    QTest::newRow("const-dropping2")
        << &QTest::QExternalTest::tryCompileFail
        << "QSharedPointer<const Data> baseptr = QSharedPointer<const Data>(new Data);\n"
           "QSharedPointer<Data> ptr;"
           "ptr = baseptr;";
    QTest::newRow("const-dropping-static-cast")
        << &QTest::QExternalTest::tryCompileFail
        << "QSharedPointer<const Data> baseptr = QSharedPointer<const Data>(new Data);\n"
        "qSharedPointerCast<DerivedData>(baseptr);";
#ifndef QTEST_NO_RTTI
    QTest::newRow("const-dropping-dynamic-cast")
        << &QTest::QExternalTest::tryCompileFail
        << "QSharedPointer<const Data> baseptr = QSharedPointer<const Data>(new Data);\n"
        "qSharedPointerDynamicCast<DerivedData>(baseptr);";
#endif
    QTest::newRow("const-dropping-object-cast1")
        << &QTest::QExternalTest::tryCompileFail
        << "QSharedPointer<const QObject> baseptr = QSharedPointer<const QObject>(new QObject);\n"
        "qSharedPointerObjectCast<QCoreApplication>(baseptr);";
    QTest::newRow("const-dropping-object-cast2")
        << &QTest::QExternalTest::tryCompileFail
        << "QSharedPointer<const QObject> baseptr = QSharedPointer<const QObject>(new QObject);\n"
        "qobject_cast<QCoreApplication *>(baseptr);";

    // arithmethics through automatic cast operators
    QTest::newRow("arithmethic1")
        << &QTest::QExternalTest::tryCompileFail
        << "QSharedPointer<int> a;"
           "QSharedPointer<Data> b;\n"
           "if (a == b) return;";
    QTest::newRow("arithmethic2")
        << &QTest::QExternalTest::tryCompileFail
        << "QSharedPointer<int> a;"
           "QSharedPointer<Data> b;\n"
           "if (a + b) return;";

    // two objects with the same pointer
    QTest::newRow("same-pointer")
        << &QTest::QExternalTest::tryRunFail
        << "Data *aData = new Data;\n"
           "QSharedPointer<Data> ptr1 = QSharedPointer<Data>(aData);\n"
           "QSharedPointer<Data> ptr2 = QSharedPointer<Data>(aData);\n";

    // two QObjects with the same pointer
    QTest::newRow("same-pointer-to-qobject")
        << &QTest::QExternalTest::tryRunFail
        << "QObject *anObj = new QObject;\n"
           "QSharedPointer<QObject> ptr1 = QSharedPointer<QObject>(anObj);\n"
           "QSharedPointer<QObject> ptr2 = QSharedPointer<QObject>(anObj);\n";

    // re-creation:
    QTest::newRow("re-creation")
        << &QTest::QExternalTest::tryRunFail
        << "Data *aData = new Data;\n"
           "QSharedPointer<Data> ptr1 = QSharedPointer<Data>(aData);"
           "ptr1 = QSharedPointer<Data>(aData);";

    // any type of cast for unrelated types:
    // (we have no reinterpret_cast)
    QTest::newRow("invalid-cast1")
        << &QTest::QExternalTest::tryCompileFail
        << "QSharedPointer<Data> ptr1;\n"
           "QSharedPointer<int> ptr2 = qSharedPointerCast<int>(ptr1);";
#ifndef QTEST_NO_RTTI
    QTest::newRow("invalid-cast2")
        << &QTest::QExternalTest::tryCompileFail
        << "QSharedPointer<Data> ptr1;\n"
           "QSharedPointer<int> ptr2 = qSharedPointerDynamicCast<int>(ptr1);";
#endif
    QTest::newRow("invalid-cast3")
        << &QTest::QExternalTest::tryCompileFail
        << "QSharedPointer<Data> ptr1;\n"
           "QSharedPointer<int> ptr2 = qSharedPointerConstCast<int>(ptr1);";
    QTest::newRow("invalid-cast4")
        << &QTest::QExternalTest::tryCompileFail
        << "QSharedPointer<Data> ptr1;\n"
           "QSharedPointer<int> ptr2 = qSharedPointerObjectCast<int>(ptr1);";

    QTest::newRow("weak-pointer-from-regular-pointer")
        << &QTest::QExternalTest::tryCompileFail
        << "Data *ptr = nullptr;\n"
           "QWeakPointer<Data> weakptr(ptr);\n";

    QTest::newRow("shared-pointer-implicit-from-uninitialized")
        << &QTest::QExternalTest::tryCompileFail
        << "Data *ptr = nullptr;\n"
           "QSharedPointer<Data> weakptr = Qt::Uninitialized;\n";

    QTest::newRow("incompatible-custom-deleter1")
        << &QTest::QExternalTest::tryCompileFail
        << "extern void incompatibleCustomDeleter(int *);\n"
           "QSharedPointer<Data> ptr(new Data, incompatibleCustomDeleter);\n";
    QTest::newRow("incompatible-custom-deleter2")
        << &QTest::QExternalTest::tryCompileFail
        << "struct IncompatibleCustomDeleter { void operator()(int *); };\n"
           "QSharedPointer<Data> ptr(new Data, IncompatibleCustomDeleter());\n";
    QTest::newRow("incompatible-custom-lambda-deleter")
        << &QTest::QExternalTest::tryCompileFail
        << "QSharedPointer<Data> ptr(new Data, [](int *) {});\n";

    QTest::newRow("incompatible-overload")
        << &QTest::QExternalTest::tryCompileFail
        << "void foo(QSharedPointer<DerivedData>) {}\n"
           "void bar() { foo(QSharedPointer<Data>()); }\n";
}

void tst_QSharedPointer::invalidConstructs()
{
#ifdef Q_CC_MINGW
    QSKIP("The maintainer of QSharedPointer: 'We don't know what the problem is so skip the tests.'");
#endif
#ifdef QTEST_CROSS_COMPILED
    QSKIP("This test does not work on cross compiled systems");
#endif

    QTest::QExternalTest test;
    test.setQtModules(QTest::QExternalTest::QtCore);
    test.setProgramHeader(
        "#define QT_SHAREDPOINTER_TRACK_POINTERS\n"
        "#define QT_DEBUG\n"
        "#include <QtCore/qsharedpointer.h>\n"
        "#include <QtCore/qcoreapplication.h>\n"
        "\n"
        "struct Data { int i; };\n"
        "struct DerivedData: public Data { int j; };\n"
        "\n"
        "class ForwardDeclared;\n"
        );

    QFETCH(QString, code);
    static bool sane = true;
    if (code.isEmpty()) {
        if (!test.tryRun("")
            || !test.tryRunFail("exit(1);")
            || !test.tryRun("QSharedPointer<Data> baseptr; QSharedPointer<DerivedData> ptr;")) {
            sane = false;
            qWarning("Sanity checking failed\nCode:\n%s\n",
                     qPrintable(test.errorReport()));
        }
    }
    if (!sane)
        QFAIL("External testing failed sanity checking, cannot proceed");

    QFETCH(TestFunction, testFunction);

    QByteArray body = code.toLatin1();

    bool result = (test.*testFunction)(body);
    if (!result || qgetenv("QTEST_EXTERNAL_DEBUG").toInt() > 0) {
        qDebug("External test output:");
#ifdef Q_CC_MSVC
        // MSVC prints errors to stdout
        printf("%s\n", test.standardOutput().constData());
#endif
        printf("%s\n", test.standardError().constData());
    }
    if (!result) {
        qWarning("External code testing failed\nCode:\n%s\n", body.constData());
        QFAIL("Fail");
    }
}
#endif // #if 0

void tst_QSharedPointer::qvariantCast()
{
    QSharedPointer<QFile> sp = QSharedPointer<QFile>::create();
    sp->setObjectName("A test name");
    QVariant v = QVariant::fromValue(sp);

    {
        QSharedPointer<QObject> other = qSharedPointerFromVariant<QObject>(v);
        QCOMPARE(other->objectName(), QString::fromLatin1("A test name"));
    }
    {
        QSharedPointer<QIODevice> other = qSharedPointerFromVariant<QIODevice>(v);
        QCOMPARE(other->objectName(), QString::fromLatin1("A test name"));
    }
    {
        QSharedPointer<QFile> other = qSharedPointerFromVariant<QFile>(v);
        QCOMPARE(other->objectName(), QString::fromLatin1("A test name"));
    }
    {
        QSharedPointer<QThread> other = qSharedPointerFromVariant<QThread>(v);
        QVERIFY(!other);
    }
    // Intentionally does not compile.
//     QSharedPointer<int> sop = qSharedPointerFromVariant<int>(v);
}

class SomeClass : public QEnableSharedFromThis<SomeClass>
{
public:
    SomeClass()
    {
    }

    QSharedPointer<SomeClass> getSharedPtr()
    {
        return sharedFromThis();
    }

    QSharedPointer<const SomeClass> getSharedPtr() const
    {
        return sharedFromThis();
    }

    Data data;
};

void tst_QSharedPointer::sharedFromThis()
{
    const int generations = Data::generationCounter;
    const int destructions = Data::destructorCounter;

    {
        SomeClass sc;
        QSharedPointer<SomeClass> scp = sc.sharedFromThis();
        QVERIFY(scp.isNull());
        QCOMPARE(Data::generationCounter, generations + 1);
        QCOMPARE(Data::destructorCounter, destructions);

        QSharedPointer<const SomeClass> const_scp = sc.sharedFromThis();
        QVERIFY(const_scp.isNull());
        QCOMPARE(Data::generationCounter, generations + 1);
        QCOMPARE(Data::destructorCounter, destructions);

        QWeakPointer<SomeClass> wcp = sc.sharedFromThis();
        QVERIFY(wcp.isNull());
        QCOMPARE(Data::generationCounter, generations + 1);
        QCOMPARE(Data::destructorCounter, destructions);

        QWeakPointer<const SomeClass> const_wcp = sc.sharedFromThis();
        QVERIFY(const_wcp.isNull());
        QCOMPARE(Data::generationCounter, generations + 1);
        QCOMPARE(Data::destructorCounter, destructions);
    }

    QCOMPARE(Data::generationCounter, generations + 1);
    QCOMPARE(Data::destructorCounter, destructions + 1);

    {
        const SomeClass sc;
        QSharedPointer<const SomeClass> const_scp = sc.sharedFromThis();
        QVERIFY(const_scp.isNull());
        QCOMPARE(Data::generationCounter, generations + 2);
        QCOMPARE(Data::destructorCounter, destructions + 1);

        QWeakPointer<const SomeClass> const_wcp = sc.sharedFromThis();
        QVERIFY(const_wcp.isNull());
        QCOMPARE(Data::generationCounter, generations + 2);
        QCOMPARE(Data::destructorCounter, destructions + 1);
    }

    QCOMPARE(Data::generationCounter, generations + 2);
    QCOMPARE(Data::destructorCounter, destructions + 2);

    {
        SomeClass *sc = new SomeClass;
        QCOMPARE(Data::generationCounter, generations + 3);
        QCOMPARE(Data::destructorCounter, destructions + 2);

        QSharedPointer<SomeClass> scp;
        QVERIFY(scp.isNull());
        QCOMPARE(Data::generationCounter, generations + 3);
        QCOMPARE(Data::destructorCounter, destructions + 2);

        scp = sc->sharedFromThis();
        QVERIFY(scp.isNull());
        QCOMPARE(Data::generationCounter, generations + 3);
        QCOMPARE(Data::destructorCounter, destructions + 2);

        scp = QSharedPointer<SomeClass>(sc);
        QVERIFY(!scp.isNull());
        QCOMPARE(scp.data(), sc);
        QCOMPARE(Data::generationCounter, generations + 3);
        QCOMPARE(Data::destructorCounter, destructions + 2);

        QSharedPointer<SomeClass> scp2;
        QVERIFY(scp2.isNull());
        QCOMPARE(Data::generationCounter, generations + 3);
        QCOMPARE(Data::destructorCounter, destructions + 2);

        scp2 = sc->sharedFromThis();
        QVERIFY(!scp2.isNull());
        QVERIFY(scp == scp2);
        QCOMPARE(scp2.data(), sc);
        QCOMPARE(Data::generationCounter, generations + 3);
        QCOMPARE(Data::destructorCounter, destructions + 2);

        QSharedPointer<const SomeClass> scp3;
        QVERIFY(scp3.isNull());
        QCOMPARE(Data::generationCounter, generations + 3);
        QCOMPARE(Data::destructorCounter, destructions + 2);

        scp3 = sc->sharedFromThis();
        QVERIFY(!scp3.isNull());
        QVERIFY(scp == scp3);
        QVERIFY(scp2 == scp3);
        QCOMPARE(scp3.data(), sc);
        QCOMPARE(Data::generationCounter, generations + 3);
        QCOMPARE(Data::destructorCounter, destructions + 2);

        QSharedPointer<SomeClass> scp4;
        QVERIFY(scp4.isNull());
        QCOMPARE(Data::generationCounter, generations + 3);
        QCOMPARE(Data::destructorCounter, destructions + 2);

        scp4 = sc->getSharedPtr();
        QVERIFY(!scp4.isNull());
        QVERIFY(scp == scp4);
        QVERIFY(scp2 == scp4);
        QVERIFY(scp3 == scp4);
        QCOMPARE(scp4.data(), sc);
        QCOMPARE(Data::generationCounter, generations + 3);
        QCOMPARE(Data::destructorCounter, destructions + 2);

        QSharedPointer<const SomeClass> scp5;
        QVERIFY(scp5.isNull());
        QCOMPARE(Data::generationCounter, generations + 3);
        QCOMPARE(Data::destructorCounter, destructions + 2);

        scp5 = const_cast<const SomeClass *>(sc)->getSharedPtr();
        QVERIFY(!scp4.isNull());
        QVERIFY(scp == scp5);
        QVERIFY(scp2 == scp5);
        QVERIFY(scp3 == scp5);
        QVERIFY(scp4 == scp5);
        QCOMPARE(scp5.data(), sc);
        QCOMPARE(Data::generationCounter, generations + 3);
        QCOMPARE(Data::destructorCounter, destructions + 2);
    }

    QCOMPARE(Data::generationCounter, generations + 3);
    QCOMPARE(Data::destructorCounter, destructions + 3);

    QSharedPointer<SomeClass> scp;

    QVERIFY(scp.isNull());

    {
        QSharedPointer<SomeClass> scp2(new SomeClass());
        QVERIFY(!scp2.isNull());

        scp = scp2->sharedFromThis();
        QVERIFY(!scp.isNull());

        QVERIFY(scp == scp2);
        QCOMPARE(Data::generationCounter, generations + 4);
        QCOMPARE(Data::destructorCounter, destructions + 3);
    }


    QCOMPARE(Data::generationCounter, generations + 4);
    QCOMPARE(Data::destructorCounter, destructions + 3);
    QVERIFY(!scp.isNull());

    {
        QSharedPointer<const SomeClass> scp2;
        scp2 = scp->sharedFromThis();
        QVERIFY(!scp2.isNull());

        QVERIFY(scp == scp2);
        QCOMPARE(Data::generationCounter, generations + 4);
        QCOMPARE(Data::destructorCounter, destructions + 3);
    }

    QCOMPARE(Data::generationCounter, generations + 4);
    QCOMPARE(Data::destructorCounter, destructions + 3);
    QVERIFY(!scp.isNull());

    {
        QSharedPointer<SomeClass> scp2;
        scp2 = scp->getSharedPtr();
        QVERIFY(!scp2.isNull());

        QVERIFY(scp == scp2);
        QCOMPARE(Data::generationCounter, generations + 4);
        QCOMPARE(Data::destructorCounter, destructions + 3);
    }

    QCOMPARE(Data::generationCounter, generations + 4);
    QCOMPARE(Data::destructorCounter, destructions + 3);
    QVERIFY(!scp.isNull());

    {
        QSharedPointer<const SomeClass> scp2;
        scp2 = qSharedPointerConstCast<const SomeClass>(scp)->getSharedPtr();
        QVERIFY(!scp2.isNull());

        QVERIFY(scp == scp2);
        QCOMPARE(Data::generationCounter, generations + 4);
        QCOMPARE(Data::destructorCounter, destructions + 3);
    }

    QCOMPARE(Data::generationCounter, generations + 4);
    QCOMPARE(Data::destructorCounter, destructions + 3);
    QVERIFY(!scp.isNull());

    {
        QSharedPointer<SomeClass> scp2;
        scp2 = scp->sharedFromThis();
        QVERIFY(!scp2.isNull());

        QVERIFY(scp == scp2);
        QCOMPARE(Data::generationCounter, generations + 4);
        QCOMPARE(Data::destructorCounter, destructions + 3);

        scp2.clear();

        QCOMPARE(Data::generationCounter, generations + 4);
        QCOMPARE(Data::destructorCounter, destructions + 3);
        QVERIFY(!scp.isNull());
        QVERIFY(scp2.isNull());
    }

    QCOMPARE(Data::generationCounter, generations + 4);
    QCOMPARE(Data::destructorCounter, destructions + 3);
    QVERIFY(!scp.isNull());

    scp.clear();

    QCOMPARE(Data::generationCounter, generations + 4);
    QCOMPARE(Data::destructorCounter, destructions + 4);

    {
        QSharedPointer<SomeClass> scp2 = QSharedPointer<SomeClass>::create();
        QVERIFY(!scp2.isNull());

        scp = scp2->sharedFromThis();
        QVERIFY(!scp.isNull());

        QVERIFY(scp == scp2);
        QCOMPARE(Data::generationCounter, generations + 5);
        QCOMPARE(Data::destructorCounter, destructions + 4);
    }
    QCOMPARE(Data::generationCounter, generations + 5);
    QCOMPARE(Data::destructorCounter, destructions + 4);

    scp.clear();

    QCOMPARE(Data::generationCounter, generations + 5);
    QCOMPARE(Data::destructorCounter, destructions + 5);

    {
        QSharedPointer<const SomeClass> scp2(new SomeClass());
        QVERIFY(!scp2.isNull());
        QCOMPARE(Data::generationCounter, generations + 6);
        QCOMPARE(Data::destructorCounter, destructions + 5);

        QWeakPointer<const SomeClass> wcp2(scp2.constCast<SomeClass>());
        QVERIFY(!wcp2.isNull());
        QCOMPARE(Data::generationCounter, generations + 6);
        QCOMPARE(Data::destructorCounter, destructions + 5);
    }

    QCOMPARE(Data::generationCounter, generations + 6);
    QCOMPARE(Data::destructorCounter, destructions + 6);
}

#ifndef QT_NO_EXCEPTIONS
class ThrowData: public Data
{
public:
    static int childDestructorCounter;
    static int childGenerationCounter;

    ThrowData()
    {
        childGenerationCounter++;
        throw QStringLiteral("Dummy exception");
    }

    ~ThrowData()
    {
        childDestructorCounter++;
    }
};
int ThrowData::childDestructorCounter = 0;
int ThrowData::childGenerationCounter = 0;
#endif // !QT_NO_EXCEPTIONS

void tst_QSharedPointer::constructorThrow()
{
#ifndef QT_NO_EXCEPTIONS
    int generation = Data::generationCounter;
    int destructorCounter = Data::destructorCounter;

    int childGeneration = ThrowData::childGenerationCounter;
    int childDestructorCounter = ThrowData::childDestructorCounter;

    QSharedPointer<ThrowData> ptr;
    QVERIFY_THROWS_EXCEPTION(QString, ptr = QSharedPointer<ThrowData>::create());
    QVERIFY(ptr.isNull());
    QCOMPARE(ThrowData::childGenerationCounter, childGeneration + 1);
    // destructor should never be called, if a constructor throws
    // an exception
    QCOMPARE(ThrowData::childDestructorCounter, childDestructorCounter);

    QCOMPARE(Data::generationCounter, generation + 1);
    // but base class constructor doesn't throw, so base class destructor
    // should be called
    QCOMPARE(Data::destructorCounter, destructorCounter + 1);
#else
    QSKIP("Needs exceptions");
#endif // !QT_NO_EXCEPTIONS
}

namespace ReentrancyWhileDestructing {
    struct IB
    {
        virtual ~IB() {}
    };

    struct IA
    {
        virtual QSharedPointer<IB> getB() = 0;
    };

    struct B: public IB
    {
        IA *m_a;
        B(IA *a_a) :m_a(a_a)
        { }
        ~B()
        {
            QSharedPointer<IB> b = m_a->getB();
        }
    };

    struct A: public IA
    {
        QSharedPointer<IB> b;

        virtual QSharedPointer<IB> getB() override
        {
            return b;
        }

        A()
        {
            b = QSharedPointer<IB>(new B(this));
        }

        ~A()
        {
            b.clear();
        }
    };
}

// This is a regression test for QTBUG-11730, where there would be a crash if
// the destructor of a QSharedPointer object being deleted recursed back into
// the same QSharedPointer object.  There are no explicit verification steps
// in this test -- it is sufficient that the code does not crash.
void tst_QSharedPointer::reentrancyWhileDestructing()
{
    ReentrancyWhileDestructing::A obj;
}

namespace {
struct Base1 {};
struct Base2 {};

struct Child1 : Base1 {};
struct Child2 : Base2 {};

template<template<typename> class SmartPtr>
struct Overloaded
{
    std::array<int, 1> call(const SmartPtr<const Base1> &)
    {
        return {};
    }
    std::array<int, 2> call(const SmartPtr<const Base2> &)
    {
        return {};
    }
    static const constexpr uint base1Called = sizeof(std::array<int, 1>);
    static const constexpr uint base2Called = sizeof(std::array<int, 2>);

    void test()
    {
#define QVERIFY_CALLS(expr, base) static_assert(sizeof(call(expr)) == base##Called)
        QVERIFY_CALLS(SmartPtr<Base1>{}, base1);
        QVERIFY_CALLS(SmartPtr<Base2>{}, base2);
        QVERIFY_CALLS(SmartPtr<const Base1>{}, base1);
        QVERIFY_CALLS(SmartPtr<const Base2>{}, base2);
        QVERIFY_CALLS(SmartPtr<Child1>{}, base1);
        QVERIFY_CALLS(SmartPtr<Child2>{}, base2);
        QVERIFY_CALLS(SmartPtr<const Child1>{}, base1);
        QVERIFY_CALLS(SmartPtr<const Child2>{}, base2);
#undef QVERIFY_CALLS
    }
};
}

void tst_QSharedPointer::overloads()
{
    Overloaded<QSharedPointer> sharedOverloaded;
    sharedOverloaded.test();
    Overloaded<QWeakPointer> weakOverloaded;
    weakOverloaded.test();
}

void tst_QSharedPointer::ownerComparisons()
{
    using SP = QSharedPointer<int>;
    using WP = QWeakPointer<int>;

#define CHECK_EQ(a, b) \
    do { \
        QVERIFY(a.owner_equal(b)); \
        QVERIFY(b.owner_equal(a)); \
        QVERIFY(!a.owner_before(b)); \
        QVERIFY(!b.owner_before(a)); \
        QVERIFY(a.owner_hash() == b.owner_hash()); \
    } while (false)

#define CHECK_NOT_EQ(a, b) \
    do { \
        QVERIFY(!a.owner_equal(b)); \
        QVERIFY(!b.owner_equal(a)); \
        QVERIFY(a.owner_before(b) || b.owner_before(a)); \
    } while (false)

    // null
    {
        SP sp1;
        SP sp2;
        WP wp1 = sp1;
        WP wp2;

        CHECK_EQ(sp1, sp1);
        CHECK_EQ(sp1, sp2);
        CHECK_EQ(sp1, wp1);
        CHECK_EQ(sp2, wp2);
        CHECK_EQ(wp1, wp1);
        CHECK_EQ(wp1, wp2);
        CHECK_EQ(wp2, wp2);
    }

    // same owner
    {
        SP sp1 = SP::create(123);
        SP sp2 = sp1;
        WP wp1 = sp1;
        SP wp2 = sp2;

        CHECK_EQ(sp1, sp1);
        CHECK_EQ(sp1, sp2);
        CHECK_EQ(sp1, wp1);
        CHECK_EQ(sp2, wp2);
        CHECK_EQ(wp1, wp1);
        CHECK_EQ(wp1, wp2);
    }

    // owning vs null
    {
        SP sp1 = SP::create(123);
        SP sp2;
        WP wp1 = sp1;
        WP wp2 = sp2;

        CHECK_EQ(sp1, sp1);
        CHECK_NOT_EQ(sp1, sp2);
        CHECK_EQ(sp1, wp1);
        CHECK_EQ(sp2, wp2);
        CHECK_EQ(wp1, wp1);
        CHECK_NOT_EQ(wp1, wp2);
    }

    // different owners
    {
        SP sp1 = SP::create(123);
        SP sp2 = SP::create(456);
        WP wp1 = sp1;
        WP wp2 = sp2;

        CHECK_EQ(sp1, sp1);
        CHECK_NOT_EQ(sp1, sp2);
        CHECK_EQ(sp1, wp1);
        CHECK_EQ(sp2, wp2);
        CHECK_EQ(wp1, wp1);
        CHECK_NOT_EQ(wp1, wp2);
    }

    // reset vs. null
    {
        SP sp1 = SP::create(123);
        SP sp2;
        WP wp1 = sp1;
        WP wp2;

        CHECK_EQ(sp1, sp1);
        CHECK_NOT_EQ(sp1, sp2);
        CHECK_EQ(sp1, wp1);
        CHECK_NOT_EQ(sp1, wp2);
        CHECK_EQ(wp1, wp1);
        CHECK_NOT_EQ(wp1, wp2);

        sp1.reset();

        CHECK_EQ(sp1, sp1);
        CHECK_EQ(sp1, sp2);
        CHECK_NOT_EQ(sp1, wp1);
        CHECK_EQ(sp2, wp2);
        CHECK_EQ(wp1, wp1);
        CHECK_NOT_EQ(wp1, wp2);
    }

    // expired weak pointers
    {
        WP wp1 = SP::create(123);
        WP wp2;

        CHECK_EQ(wp1, wp1);
        CHECK_NOT_EQ(wp1, wp2);
    }

    {
        WP wp1 = SP::create(123);
        WP wp2 = wp1;

        CHECK_EQ(wp1, wp1);
        CHECK_EQ(wp1, wp2);
    }

    {
        WP wp1 = SP::create(123);
        WP wp2 = SP::create(456);

        CHECK_EQ(wp1, wp1);
        CHECK_EQ(wp2, wp2);
        CHECK_NOT_EQ(wp1, wp2);
    }
#undef CHECK_EQ
#undef CHECK_NOT_EQ
}

QTEST_MAIN(tst_QSharedPointer)
#include "tst_qsharedpointer.moc"
