b8d656d878
This is in preparation of removing testlib's dependency on QtGui and QtWidgets. Autotests that need QtWidgets api must explicitly include it (since the types are no longer provided by the QtGui master header). Autotests that don't need QtGui or QtWidgets api shouldn't link against those libraries. Change-Id: I2808289068514fcac582808828ad4634e2631733 Reviewed-on: http://codereview.qt-project.org/5093 Reviewed-by: Qt Sanity Bot <qt_sanity_bot@ovi.com> Reviewed-by: Lars Knoll <lars.knoll@nokia.com>
813 lines
22 KiB
C++
813 lines
22 KiB
C++
/****************************************************************************
|
|
**
|
|
** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
|
|
** All rights reserved.
|
|
** Contact: Nokia Corporation (qt-info@nokia.com)
|
|
**
|
|
** This file is part of the test suite of the Qt Toolkit.
|
|
**
|
|
** $QT_BEGIN_LICENSE:LGPL$
|
|
** GNU Lesser General Public License Usage
|
|
** This file may be used under the terms of the GNU Lesser General Public
|
|
** License version 2.1 as published by the Free Software Foundation and
|
|
** appearing in the file LICENSE.LGPL included in the packaging of this
|
|
** file. Please review the following information to ensure the GNU Lesser
|
|
** General Public License version 2.1 requirements will be met:
|
|
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
|
**
|
|
** In addition, as a special exception, Nokia gives you certain additional
|
|
** rights. These rights are described in the Nokia Qt LGPL Exception
|
|
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
|
**
|
|
** GNU General Public License Usage
|
|
** Alternatively, this file may be used under the terms of the GNU General
|
|
** Public License version 3.0 as published by the Free Software Foundation
|
|
** and appearing in the file LICENSE.GPL included in the packaging of this
|
|
** file. Please review the following information to ensure the GNU General
|
|
** Public License version 3.0 requirements will be met:
|
|
** http://www.gnu.org/copyleft/gpl.html.
|
|
**
|
|
** Other Usage
|
|
** Alternatively, this file may be used in accordance with the terms and
|
|
** conditions contained in a signed written agreement between you and Nokia.
|
|
**
|
|
**
|
|
**
|
|
**
|
|
**
|
|
** $QT_END_LICENSE$
|
|
**
|
|
****************************************************************************/
|
|
|
|
#include <QtGui/QtGui>
|
|
#include <QtWidgets/QtWidgets>
|
|
#include <QtTest/QtTest>
|
|
|
|
#include <stddef.h>
|
|
#include <exception>
|
|
|
|
QT_USE_NAMESPACE
|
|
|
|
// this test only works with
|
|
// * GLIBC
|
|
// * MSVC - only debug builds (we need the crtdbg.h helpers)
|
|
// * SYMBIAN
|
|
#if (defined(QT_NO_EXCEPTIONS) || (!defined(__GLIBC__) && !defined(Q_CC_MSVC) && !defined(Q_OS_SYMBIAN))) && !defined(Q_MOC_RUN)
|
|
QTEST_NOOP_MAIN
|
|
#else
|
|
|
|
#include "oomsimulator.h"
|
|
#if !defined(Q_OS_SYMBIAN)
|
|
#include "3rdparty/memcheck.h"
|
|
#endif
|
|
|
|
class tst_ExceptionSafety_Objects: public QObject
|
|
{
|
|
Q_OBJECT
|
|
|
|
public slots:
|
|
void initTestCase();
|
|
void cleanupTestCase();
|
|
|
|
private slots:
|
|
void objects_data();
|
|
void objects();
|
|
|
|
void widgets_data();
|
|
void widgets();
|
|
|
|
void vector_data();
|
|
void vector();
|
|
|
|
void list_data();
|
|
void list();
|
|
|
|
void linkedList_data();
|
|
void linkedList();
|
|
|
|
private:
|
|
static QtMsgHandler testMessageHandler;
|
|
static void safeMessageHandler(QtMsgType, const char *);
|
|
};
|
|
|
|
// helper structs to create an arbitrary widget
|
|
struct AbstractTester
|
|
{
|
|
virtual void operator()(QObject *parent) = 0;
|
|
};
|
|
Q_DECLARE_METATYPE(AbstractTester *)
|
|
|
|
typedef void (*TestFunction)(QObject*);
|
|
Q_DECLARE_METATYPE(TestFunction)
|
|
|
|
template <typename T>
|
|
struct ObjectCreator : public AbstractTester
|
|
{
|
|
void operator()(QObject *)
|
|
{
|
|
QScopedPointer<T> ptr(new T);
|
|
}
|
|
};
|
|
|
|
struct BitArrayCreator : public AbstractTester
|
|
{
|
|
void operator()(QObject *)
|
|
{ QScopedPointer<QBitArray> bitArray(new QBitArray(100, true)); }
|
|
};
|
|
|
|
struct ByteArrayMatcherCreator : public AbstractTester
|
|
{
|
|
void operator()(QObject *)
|
|
{ QScopedPointer<QByteArrayMatcher> ptr(new QByteArrayMatcher("ralf test",8)); }
|
|
};
|
|
|
|
struct CryptographicHashCreator : public AbstractTester
|
|
{
|
|
void operator()(QObject *)
|
|
{
|
|
QScopedPointer<QCryptographicHash> ptr(new QCryptographicHash(QCryptographicHash::Sha1));
|
|
ptr->addData("ralf test",8);
|
|
}
|
|
};
|
|
|
|
struct DataStreamCreator : public AbstractTester
|
|
{
|
|
void operator()(QObject *)
|
|
{
|
|
QScopedPointer<QByteArray> arr(new QByteArray("hallo, test"));
|
|
QScopedPointer<QDataStream> ptr(new QDataStream(arr.data(), QIODevice::ReadWrite));
|
|
ptr->writeBytes("ralf test",8);
|
|
}
|
|
};
|
|
|
|
struct DirCreator : public AbstractTester
|
|
{
|
|
void operator()(QObject *)
|
|
{
|
|
QDir::cleanPath("../////././");
|
|
QScopedPointer<QDir> ptr(new QDir("."));
|
|
while( ptr->cdUp() )
|
|
; // just going up
|
|
ptr->count();
|
|
ptr->exists(ptr->path());
|
|
|
|
QStringList filters;
|
|
filters << "*.cpp" << "*.cxx" << "*.cc";
|
|
ptr->setNameFilters(filters);
|
|
}
|
|
};
|
|
|
|
void tst_ExceptionSafety_Objects::objects_data()
|
|
{
|
|
QTest::addColumn<AbstractTester *>("objectCreator");
|
|
|
|
#define NEWROW(T) QTest::newRow(#T) << static_cast<AbstractTester *>(new ObjectCreator<T >)
|
|
NEWROW(QObject);
|
|
NEWROW(QBuffer);
|
|
NEWROW(QFile);
|
|
NEWROW(QFSFileEngine);
|
|
NEWROW(QProcess);
|
|
NEWROW(QSettings);
|
|
NEWROW(QThread);
|
|
NEWROW(QThreadPool);
|
|
NEWROW(QTranslator);
|
|
|
|
#define NEWROW2(T, CREATOR) QTest::newRow(#T) << static_cast<AbstractTester *>(new CREATOR)
|
|
NEWROW2(QBitArray, BitArrayCreator);
|
|
NEWROW2(QByteArrayMatcher, ByteArrayMatcherCreator);
|
|
NEWROW2(QCryptographicHash, CryptographicHashCreator);
|
|
NEWROW2(QDataStream, DataStreamCreator);
|
|
NEWROW2(QDir, DirCreator);
|
|
}
|
|
|
|
// create and destructs an object, and lets each and every allocation
|
|
// during construction and destruction fail.
|
|
template <typename T>
|
|
static void doOOMTest(T &testFunc, QObject *parent, int start=0)
|
|
{
|
|
int currentOOMIndex = start;
|
|
bool caught = false;
|
|
bool done = false;
|
|
|
|
AllocFailer allocFailer(0);
|
|
int allocCountBefore = allocFailer.currentAllocIndex();
|
|
|
|
do {
|
|
allocFailer.reactivateAt(++currentOOMIndex);
|
|
|
|
caught = false;
|
|
|
|
try {
|
|
testFunc(parent);
|
|
} catch (const std::bad_alloc &) {
|
|
caught = true;
|
|
} catch (const std::exception &ex) {
|
|
if (strcmp(ex.what(), "autotest swallow") != 0)
|
|
throw;
|
|
caught = true;
|
|
}
|
|
|
|
if (!caught) {
|
|
void *buf = malloc(42);
|
|
if (buf) {
|
|
// we got memory here - oom test is over.
|
|
free(buf);
|
|
done = true;
|
|
}
|
|
}
|
|
|
|
// if we get a FAIL, stop executing now
|
|
if (QTest::currentTestFailed())
|
|
done = true;
|
|
|
|
//#define REALLY_VERBOSE
|
|
#ifdef REALLY_VERBOSE
|
|
fprintf(stderr, " OOM Index: %d\n", currentOOMIndex);
|
|
#endif
|
|
|
|
|
|
} while (caught || !done);
|
|
|
|
allocFailer.deactivate();
|
|
|
|
//#define VERBOSE
|
|
#ifdef VERBOSE
|
|
fprintf(stderr, "OOM Test done, checked allocs: %d (range %d - %d)\n", currentOOMIndex,
|
|
allocCountBefore, allocFailer.currentAllocIndex());
|
|
#else
|
|
Q_UNUSED(allocCountBefore);
|
|
#endif
|
|
}
|
|
|
|
static int alloc1Failed = 0;
|
|
static int alloc2Failed = 0;
|
|
static int alloc3Failed = 0;
|
|
static int alloc4Failed = 0;
|
|
static int malloc1Failed = 0;
|
|
static int malloc2Failed = 0;
|
|
|
|
// Tests that new, new[] and malloc() fail at least once during OOM testing.
|
|
class SelfTestObject : public QObject
|
|
{
|
|
public:
|
|
SelfTestObject(QObject *parent = 0)
|
|
: QObject(parent)
|
|
{
|
|
try { delete new int; } catch (const std::bad_alloc &) { ++alloc1Failed; throw; }
|
|
try { delete [] new double[5]; } catch (const std::bad_alloc &) { ++alloc2Failed; throw ;}
|
|
void *buf = malloc(42);
|
|
if (buf)
|
|
free(buf);
|
|
else
|
|
++malloc1Failed;
|
|
}
|
|
|
|
~SelfTestObject()
|
|
{
|
|
try { delete new int; } catch (const std::bad_alloc &) { ++alloc3Failed; }
|
|
try { delete [] new double[5]; } catch (const std::bad_alloc &) { ++alloc4Failed; }
|
|
void *buf = malloc(42);
|
|
if (buf)
|
|
free(buf);
|
|
else
|
|
++malloc2Failed = true;
|
|
}
|
|
};
|
|
|
|
QtMsgHandler tst_ExceptionSafety_Objects::testMessageHandler;
|
|
|
|
void tst_ExceptionSafety_Objects::safeMessageHandler(QtMsgType type, const char *msg)
|
|
{
|
|
// this temporarily suspends OOM testing while handling a message
|
|
int currentIndex = mallocFailIndex;
|
|
AllocFailer allocFailer(0);
|
|
allocFailer.deactivate();
|
|
(*testMessageHandler)(type, msg);
|
|
allocFailer.reactivateAt(currentIndex);
|
|
}
|
|
|
|
typedef void (*PVF)();
|
|
PVF defaultTerminate;
|
|
void debugTerminate()
|
|
{
|
|
// you can detect uncaught exceptions with a breakpoint in here
|
|
(*defaultTerminate)();
|
|
}
|
|
|
|
PVF defaultUnexpected;
|
|
void debugUnexpected()
|
|
{
|
|
// you can detect unexpected exceptions with a breakpoint in here
|
|
(*defaultUnexpected)();
|
|
}
|
|
|
|
void tst_ExceptionSafety_Objects::initTestCase()
|
|
{
|
|
// set handlers for bad exception cases, you might want to step in and breakpoint the default handlers too
|
|
defaultTerminate = std::set_terminate(&debugTerminate);
|
|
defaultUnexpected = std::set_unexpected(&debugUnexpected);
|
|
testMessageHandler = qInstallMsgHandler(safeMessageHandler);
|
|
|
|
QVERIFY(AllocFailer::initialize());
|
|
|
|
// sanity check whether OOM simulation works
|
|
AllocFailer allocFailer(0);
|
|
|
|
// malloc fail index is 0 -> this malloc should fail.
|
|
void *buf = malloc(42);
|
|
allocFailer.deactivate();
|
|
QVERIFY(!buf);
|
|
|
|
// malloc fail index is 1 - second malloc should fail.
|
|
allocFailer.reactivateAt(1);
|
|
buf = malloc(42);
|
|
void *buf2 = malloc(42);
|
|
allocFailer.deactivate();
|
|
|
|
QVERIFY(buf);
|
|
free(buf);
|
|
QVERIFY(!buf2);
|
|
|
|
#ifdef Q_OS_SYMBIAN
|
|
// temporary workaround for INC138398
|
|
std::new_handler nh_func = std::set_new_handler(0);
|
|
(void) std::set_new_handler(nh_func);
|
|
#endif
|
|
|
|
ObjectCreator<SelfTestObject> *selfTest = new ObjectCreator<SelfTestObject>;
|
|
doOOMTest(*selfTest, 0);
|
|
delete selfTest;
|
|
QCOMPARE(alloc1Failed, 1);
|
|
QCOMPARE(alloc2Failed, 1);
|
|
QCOMPARE(alloc3Failed, 2);
|
|
QCOMPARE(alloc4Failed, 3);
|
|
QCOMPARE(malloc1Failed, 1);
|
|
QCOMPARE(malloc2Failed, 1);
|
|
}
|
|
|
|
void tst_ExceptionSafety_Objects::cleanupTestCase()
|
|
{
|
|
qInstallMsgHandler(testMessageHandler);
|
|
}
|
|
|
|
void tst_ExceptionSafety_Objects::objects()
|
|
{
|
|
QLatin1String tag = QLatin1String(QTest::currentDataTag());
|
|
if (tag == QLatin1String("QFile")
|
|
|| tag == QLatin1String("QProcess")
|
|
|| tag == QLatin1String("QSettings")
|
|
|| tag == QLatin1String("QThread")
|
|
|| tag == QLatin1String("QThreadPool"))
|
|
QSKIP("This type of object is not currently strongly exception safe", SkipSingle);
|
|
|
|
QFETCH(AbstractTester *, objectCreator);
|
|
|
|
doOOMTest(*objectCreator, 0);
|
|
|
|
delete objectCreator;
|
|
}
|
|
|
|
template <typename T>
|
|
struct WidgetCreator : public AbstractTester
|
|
{
|
|
void operator()(QObject *parent)
|
|
{
|
|
if (parent && !parent->isWidgetType())
|
|
qFatal("%s: parent must be either null or a widget type", Q_FUNC_INFO);
|
|
QScopedPointer<T> ptr(parent ? new T(static_cast<QWidget *>(parent)) : new T);
|
|
}
|
|
};
|
|
|
|
// QSizeGrip doesn't have a default constructor - always pass parent (even though it might be 0)
|
|
template <> struct WidgetCreator<QSizeGrip> : public AbstractTester
|
|
{
|
|
void operator()(QObject *parent)
|
|
{
|
|
if (parent && !parent->isWidgetType())
|
|
qFatal("%s: parent must be either null or a widget type", Q_FUNC_INFO);
|
|
QScopedPointer<QSizeGrip> ptr(new QSizeGrip(static_cast<QWidget *>(parent)));
|
|
}
|
|
};
|
|
|
|
// QDesktopWidget doesn't need a parent.
|
|
template <> struct WidgetCreator<QDesktopWidget> : public AbstractTester
|
|
{
|
|
void operator()(QObject *parent)
|
|
{
|
|
if (parent && !parent->isWidgetType())
|
|
qFatal("%s: parent must be either null or a widget type", Q_FUNC_INFO);
|
|
QScopedPointer<QDesktopWidget> ptr(new QDesktopWidget());
|
|
}
|
|
};
|
|
void tst_ExceptionSafety_Objects::widgets_data()
|
|
{
|
|
#ifdef Q_OS_SYMBIAN
|
|
// Initialise the S60 rasteriser, which crashes if started while out of memory
|
|
QImage image(20, 20, QImage::Format_RGB32);
|
|
QPainter p(&image);
|
|
p.drawText(0, 15, "foo");
|
|
#endif
|
|
|
|
QTest::addColumn<AbstractTester *>("widgetCreator");
|
|
|
|
#undef NEWROW
|
|
#define NEWROW(T) QTest::newRow(#T) << static_cast<AbstractTester *>(new WidgetCreator<T >)
|
|
|
|
NEWROW(QWidget);
|
|
|
|
NEWROW(QButtonGroup);
|
|
NEWROW(QCheckBox);
|
|
NEWROW(QColumnView);
|
|
NEWROW(QComboBox);
|
|
NEWROW(QCommandLinkButton);
|
|
NEWROW(QDateEdit);
|
|
NEWROW(QDateTimeEdit);
|
|
NEWROW(QDesktopWidget);
|
|
NEWROW(QDial);
|
|
NEWROW(QDoubleSpinBox);
|
|
NEWROW(QFocusFrame);
|
|
NEWROW(QFontComboBox);
|
|
NEWROW(QFrame);
|
|
NEWROW(QGroupBox);
|
|
NEWROW(QLabel);
|
|
NEWROW(QLCDNumber);
|
|
NEWROW(QLineEdit);
|
|
NEWROW(QListView);
|
|
NEWROW(QListWidget);
|
|
NEWROW(QMainWindow);
|
|
NEWROW(QMenu);
|
|
NEWROW(QMenuBar);
|
|
NEWROW(QPlainTextEdit);
|
|
NEWROW(QProgressBar);
|
|
NEWROW(QPushButton);
|
|
NEWROW(QRadioButton);
|
|
NEWROW(QScrollArea);
|
|
NEWROW(QScrollBar);
|
|
NEWROW(QSizeGrip);
|
|
NEWROW(QSlider);
|
|
NEWROW(QSpinBox);
|
|
NEWROW(QSplitter);
|
|
NEWROW(QStackedWidget);
|
|
NEWROW(QStatusBar);
|
|
NEWROW(QTabBar);
|
|
NEWROW(QTableView);
|
|
NEWROW(QTableWidget);
|
|
NEWROW(QTabWidget);
|
|
NEWROW(QTextBrowser);
|
|
NEWROW(QTextEdit);
|
|
NEWROW(QTimeEdit);
|
|
NEWROW(QToolBar);
|
|
NEWROW(QToolBox);
|
|
NEWROW(QToolButton);
|
|
NEWROW(QTreeView);
|
|
NEWROW(QTreeWidget);
|
|
NEWROW(QWorkspace);
|
|
}
|
|
|
|
void tst_ExceptionSafety_Objects::widgets()
|
|
{
|
|
QLatin1String tag = QLatin1String(QTest::currentDataTag());
|
|
if (tag == QLatin1String("QColumnView")
|
|
|| tag == QLatin1String("QComboBox")
|
|
|| tag == QLatin1String("QCommandLinkButton")
|
|
|| tag == QLatin1String("QDateEdit")
|
|
|| tag == QLatin1String("QDateTimeEdit")
|
|
|| tag == QLatin1String("QDesktopWidget")
|
|
|| tag == QLatin1String("QDoubleSpinBox")
|
|
|| tag == QLatin1String("QFontComboBox")
|
|
|| tag == QLatin1String("QGroupBox")
|
|
|| tag == QLatin1String("QLineEdit")
|
|
|| tag == QLatin1String("QListView")
|
|
|| tag == QLatin1String("QListWidget")
|
|
|| tag == QLatin1String("QMainWindow")
|
|
|| tag == QLatin1String("QMenu")
|
|
|| tag == QLatin1String("QMenuBar")
|
|
|| tag == QLatin1String("QPlainTextEdit")
|
|
|| tag == QLatin1String("QProgressBar")
|
|
|| tag == QLatin1String("QPushButton")
|
|
|| tag == QLatin1String("QScrollArea")
|
|
|| tag == QLatin1String("QSpinBox")
|
|
|| tag == QLatin1String("QStackedWidget")
|
|
|| tag == QLatin1String("QStatusBar")
|
|
|| tag == QLatin1String("QTableView")
|
|
|| tag == QLatin1String("QTableWidget")
|
|
|| tag == QLatin1String("QTabWidget")
|
|
|| tag == QLatin1String("QTextBrowser")
|
|
|| tag == QLatin1String("QTextEdit")
|
|
|| tag == QLatin1String("QTimeEdit")
|
|
|| tag == QLatin1String("QToolBar")
|
|
|| tag == QLatin1String("QToolBox")
|
|
|| tag == QLatin1String("QTreeView")
|
|
|| tag == QLatin1String("QTreeWidget")
|
|
|| tag == QLatin1String("QWorkspace"))
|
|
QSKIP("This type of widget is not currently strongly exception safe", SkipSingle);
|
|
|
|
QFETCH(AbstractTester *, widgetCreator);
|
|
|
|
doOOMTest(*widgetCreator, 0, 00000);
|
|
|
|
QWidget parent;
|
|
doOOMTest(*widgetCreator, &parent, 00000);
|
|
|
|
delete widgetCreator;
|
|
|
|
// if the test reaches here without crashing, we passed :)
|
|
QVERIFY(true);
|
|
}
|
|
|
|
struct Integer
|
|
{
|
|
Integer(int value = 42)
|
|
: ptr(new int(value))
|
|
{
|
|
++instanceCount;
|
|
}
|
|
|
|
Integer(const Integer &other)
|
|
: ptr(new int(*other.ptr))
|
|
{
|
|
++instanceCount;
|
|
}
|
|
|
|
Integer &operator=(const Integer &other)
|
|
{
|
|
int *newPtr = new int(*other.ptr);
|
|
delete ptr;
|
|
ptr = newPtr;
|
|
return *this;
|
|
}
|
|
|
|
~Integer()
|
|
{
|
|
--instanceCount;
|
|
delete ptr;
|
|
}
|
|
|
|
int value() const
|
|
{
|
|
return *ptr;
|
|
}
|
|
|
|
int *ptr;
|
|
static int instanceCount;
|
|
};
|
|
|
|
int Integer::instanceCount = 0;
|
|
|
|
struct IntegerMoveable
|
|
{
|
|
IntegerMoveable(int value = 42)
|
|
: val(value)
|
|
{
|
|
delete new int;
|
|
++instanceCount;
|
|
}
|
|
|
|
IntegerMoveable(const IntegerMoveable &other)
|
|
: val(other.val)
|
|
{
|
|
delete new int;
|
|
++instanceCount;
|
|
}
|
|
|
|
IntegerMoveable &operator=(const IntegerMoveable &other)
|
|
{
|
|
delete new int;
|
|
val = other.val;
|
|
return *this;
|
|
}
|
|
|
|
~IntegerMoveable()
|
|
{
|
|
--instanceCount;
|
|
}
|
|
|
|
int value() const
|
|
{
|
|
return val;
|
|
}
|
|
|
|
int val;
|
|
static int instanceCount;
|
|
};
|
|
|
|
int IntegerMoveable::instanceCount = 0;
|
|
QT_BEGIN_NAMESPACE
|
|
Q_DECLARE_TYPEINFO(IntegerMoveable, Q_MOVABLE_TYPE);
|
|
QT_END_NAMESPACE
|
|
|
|
template <typename T, template<typename> class Container>
|
|
void containerInsertTest(QObject*)
|
|
{
|
|
Container<T> container;
|
|
|
|
// insert an item in an empty container
|
|
try {
|
|
container.insert(container.begin(), 41);
|
|
} catch (...) {
|
|
QVERIFY(container.isEmpty());
|
|
QCOMPARE(T::instanceCount, 0);
|
|
return;
|
|
}
|
|
|
|
QCOMPARE(container.size(), 1);
|
|
QCOMPARE(T::instanceCount, 1);
|
|
|
|
// insert an item before another item
|
|
try {
|
|
container.insert(container.begin(), 42);
|
|
} catch (...) {
|
|
QCOMPARE(container.size(), 1);
|
|
QCOMPARE(container.first().value(), 41);
|
|
QCOMPARE(T::instanceCount, 1);
|
|
return;
|
|
}
|
|
|
|
QCOMPARE(T::instanceCount, 2);
|
|
|
|
// insert an item in between
|
|
try {
|
|
container.insert(container.begin() + 1, 43);
|
|
} catch (...) {
|
|
QCOMPARE(container.size(), 2);
|
|
QCOMPARE(container.first().value(), 41);
|
|
QCOMPARE((container.begin() + 1)->value(), 42);
|
|
QCOMPARE(T::instanceCount, 2);
|
|
return;
|
|
}
|
|
|
|
QCOMPARE(T::instanceCount, 3);
|
|
}
|
|
|
|
template <typename T, template<typename> class Container>
|
|
void containerAppendTest(QObject*)
|
|
{
|
|
Container<T> container;
|
|
|
|
// append to an empty container
|
|
try {
|
|
container.append(42);
|
|
} catch (...) {
|
|
QCOMPARE(container.size(), 0);
|
|
QCOMPARE(T::instanceCount, 0);
|
|
return;
|
|
}
|
|
|
|
// append to a container with one item
|
|
try {
|
|
container.append(43);
|
|
} catch (...) {
|
|
QCOMPARE(container.size(), 1);
|
|
QCOMPARE(container.first().value(), 42);
|
|
QCOMPARE(T::instanceCount, 1);
|
|
return;
|
|
}
|
|
|
|
Container<T> container2;
|
|
|
|
try {
|
|
container2.append(44);
|
|
} catch (...) {
|
|
// don't care
|
|
return;
|
|
}
|
|
QCOMPARE(T::instanceCount, 3);
|
|
|
|
// append another container with one item
|
|
try {
|
|
container += container2;
|
|
} catch (...) {
|
|
QCOMPARE(container.size(), 2);
|
|
QCOMPARE(container.first().value(), 42);
|
|
QCOMPARE((container.begin() + 1)->value(), 43);
|
|
QCOMPARE(T::instanceCount, 3);
|
|
return;
|
|
}
|
|
|
|
QCOMPARE(T::instanceCount, 4);
|
|
}
|
|
|
|
template <typename T, template<typename> class Container>
|
|
void containerEraseTest(QObject*)
|
|
{
|
|
Container<T> container;
|
|
|
|
try {
|
|
container.append(42);
|
|
container.append(43);
|
|
container.append(44);
|
|
container.append(45);
|
|
container.append(46);
|
|
} catch (...) {
|
|
// don't care
|
|
return;
|
|
}
|
|
|
|
// sanity checks
|
|
QCOMPARE(container.size(), 5);
|
|
QCOMPARE(T::instanceCount, 5);
|
|
|
|
// delete the first one
|
|
try {
|
|
container.erase(container.begin());
|
|
} catch (...) {
|
|
QCOMPARE(container.size(), 5);
|
|
QCOMPARE(container.first().value(), 42);
|
|
QCOMPARE(T::instanceCount, 5);
|
|
return;
|
|
}
|
|
|
|
QCOMPARE(container.size(), 4);
|
|
QCOMPARE(container.first().value(), 43);
|
|
QCOMPARE(T::instanceCount, 4);
|
|
|
|
// delete the last one
|
|
try {
|
|
container.erase(container.end() - 1);
|
|
} catch (...) {
|
|
QCOMPARE(container.size(), 4);
|
|
QCOMPARE(T::instanceCount, 4);
|
|
return;
|
|
}
|
|
|
|
QCOMPARE(container.size(), 3);
|
|
QCOMPARE(container.first().value(), 43);
|
|
QCOMPARE((container.begin() + 1)->value(), 44);
|
|
QCOMPARE((container.begin() + 2)->value(), 45);
|
|
QCOMPARE(T::instanceCount, 3);
|
|
|
|
// delete the middle one
|
|
try {
|
|
container.erase(container.begin() + 1);
|
|
} catch (...) {
|
|
QCOMPARE(container.size(), 3);
|
|
QCOMPARE(container.first().value(), 43);
|
|
QCOMPARE((container.begin() + 1)->value(), 44);
|
|
QCOMPARE((container.begin() + 2)->value(), 45);
|
|
QCOMPARE(T::instanceCount, 3);
|
|
return;
|
|
}
|
|
|
|
QCOMPARE(container.size(), 2);
|
|
QCOMPARE(container.first().value(), 43);
|
|
QCOMPARE((container.begin() + 1)->value(), 45);
|
|
QCOMPARE(T::instanceCount, 2);
|
|
}
|
|
|
|
template <template<typename T> class Container>
|
|
static void containerData()
|
|
{
|
|
QTest::addColumn<TestFunction>("testFunction");
|
|
|
|
QTest::newRow("insert static") << static_cast<TestFunction>(containerInsertTest<Integer, Container>);
|
|
QTest::newRow("append static") << static_cast<TestFunction>(containerAppendTest<Integer, Container>);
|
|
QTest::newRow("erase static") << static_cast<TestFunction>(containerEraseTest<Integer, Container>);
|
|
QTest::newRow("insert moveable") << static_cast<TestFunction>(containerInsertTest<IntegerMoveable, Container>);
|
|
QTest::newRow("append moveable") << static_cast<TestFunction>(containerAppendTest<IntegerMoveable, Container>);
|
|
QTest::newRow("erase moveable") << static_cast<TestFunction>(containerEraseTest<IntegerMoveable, Container>);
|
|
}
|
|
|
|
void tst_ExceptionSafety_Objects::vector_data()
|
|
{
|
|
containerData<QVector>();
|
|
}
|
|
|
|
void tst_ExceptionSafety_Objects::vector()
|
|
{
|
|
QFETCH(TestFunction, testFunction);
|
|
|
|
if (QLatin1String(QTest::currentDataTag()) == QLatin1String("insert static")
|
|
|| QLatin1String(QTest::currentDataTag()) == QLatin1String("insert moveable"))
|
|
QSKIP("QVector::insert is currently not strongly exception safe", SkipSingle);
|
|
|
|
doOOMTest(testFunction, 0);
|
|
}
|
|
|
|
void tst_ExceptionSafety_Objects::list_data()
|
|
{
|
|
containerData<QList>();
|
|
}
|
|
|
|
void tst_ExceptionSafety_Objects::list()
|
|
{
|
|
QFETCH(TestFunction, testFunction);
|
|
|
|
doOOMTest(testFunction, 0);
|
|
}
|
|
|
|
void tst_ExceptionSafety_Objects::linkedList_data()
|
|
{
|
|
containerData<QLinkedList>();
|
|
}
|
|
|
|
void tst_ExceptionSafety_Objects::linkedList()
|
|
{
|
|
QFETCH(TestFunction, testFunction);
|
|
|
|
doOOMTest(testFunction, 0);
|
|
}
|
|
|
|
QTEST_MAIN(tst_ExceptionSafety_Objects)
|
|
#include "tst_exceptionsafety_objects.moc"
|
|
#endif // QT_NO_EXCEPTIONS
|