tst_QScopedPointer: add a test for reset/delete ordering

We have implementation divergence with libc++'s ~unique_ptr()
behavior, so make sure QScopedPointer behaves the same way as
unique_ptr is spec'ed, even though to check it, one has to rely
on UB, at least according to LLVM devs (I don't think the test
as written invokes UB).

Code's a bit duplicated. I tried making the struct a template,
but it's just too much of a fuss (needs a variable template,
which we can't use in Qt 5), so I decided to use cut'n'paste.

Task-number: QTBUG-137069
Pick-to: 6.8 6.5 5.15
Change-Id: I8b55718eaf3c6ad9a4d89d4fc9d64f0c41bb21fa
Reviewed-by: Ahmad Samir <a.samirh78@gmail.com>
(cherry picked from commit 82015992c853b50dac167da26b8b858ac4794c66)
Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
This commit is contained in:
Marc Mutz 2025-05-21 13:14:20 +02:00 committed by Qt Cherry-pick Bot
parent 8f6a286fff
commit 8d81da76be

View File

@ -4,6 +4,8 @@
#include <QTest>
#include <QtCore/QScopedPointer>
#include <QtCore/qstdlibdetection.h>
/*!
\class tst_QScopedPointer
\internal
@ -22,6 +24,7 @@ private Q_SLOTS:
void dataOnValue();
void dataSignature();
void reset();
void resetDeleteOrdering();
void dereferenceOperator();
void dereferenceOperatorSignature();
void pointerOperator();
@ -127,6 +130,85 @@ void tst_QScopedPointer::reset()
}
}
void tst_QScopedPointer::resetDeleteOrdering()
{
{
struct CheckingDeleter;
using SP = QScopedPointer<int, CheckingDeleter>;
static const SP *instance = nullptr; // QScopedPointer does not handle stateful deleters
static const int *expected = nullptr;
struct CheckingDeleter {
static void cleanup(int *p)
{
const auto *value = instance->get();
std::unique_ptr<int> deleter(p);
QT_TRY { QCOMPARE(value, expected); } QT_CATCH(...) {} // eat QTest failure exception, if any
}
void operator()(int *p) { cleanup(p); }
};
// reset()
{
SP p(new int{42});
instance = &p;
expected = nullptr;
p.reset();
}
if (QTest::currentTestFailed())
return;
// destructor
{
SP p(new int{48});
instance = &p;
expected = p.get(); // inconsistent with reset(), but consistent with unique_ptr
}
if (QTest::currentTestFailed())
return;
}
// comparison with unique_ptr:
{
struct CheckingDeleter;
using UP = std::unique_ptr<int, CheckingDeleter>;
// unique_ptr handles stateful deleters, but be consistent with QScopedPointer test
static const UP *instance = nullptr;
static const int *expected = nullptr;
struct CheckingDeleter {
void operator()(int *p) const
{
const auto *value = instance->get();
std::unique_ptr<int> deleter(p);
QT_TRY { QCOMPARE(value, expected); } QT_CATCH(...) {} // eat QTest failure exception, if any
}
};
// reset()
{
UP p(new int{42});
instance = &p;
expected = nullptr; // https://eel.is/c++draft/unique.ptr#single.modifiers-3 ... 5
p.reset();
}
if (QTest::currentTestFailed())
return;
// destructor
#ifdef Q_STL_LIBCPP
// This would fail on libc++ https://github.com/llvm/llvm-project/issues/108149
if (false)
#endif
{
UP p(new int{48});
instance = &p;
expected = p.get(); // https://eel.is/c++draft/unique.ptr#single.dtor
}
if (QTest::currentTestFailed())
return;
}
}
class AbstractClass
{
public: