Remove exception safety tests

During Qt Contributor Summit 2013 we agreed that we will not support
exception safety anymore.

http://comments.gmane.org/gmane.comp.lib.qt.devel/12004

Task-number: QTBUG-32642
Change-Id: If57917fe8af45e787e215431c94579bc86fc7683
Reviewed-by: Simo Fält <simo.falt@digia.com>
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
This commit is contained in:
Jędrzej Nowacki 2013-08-06 09:39:53 +02:00 committed by The Qt Project
parent fe375dc3d0
commit 82e976d52c
9 changed files with 0 additions and 6201 deletions

View File

@ -1 +0,0 @@
tst_exceptionsafety

View File

@ -1,6 +0,0 @@
CONFIG += testcase
TARGET = tst_exceptionsafety
SOURCES += tst_exceptionsafety.cpp
QT = core testlib
CONFIG += parallel_test
DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0

View File

@ -1,846 +0,0 @@
/****************************************************************************
**
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of the test suite of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia. For licensing terms and
** conditions see http://qt.digia.com/licensing. For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights. These rights are described in the Digia 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.
**
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include "qplatformdefs.h"
#include <stdlib.h>
void* internalMalloc(size_t bytes);
#define malloc internalMalloc
#include <QVarLengthArray>
#undef malloc
#include <QtTest/QtTest>
QT_USE_NAMESPACE
class tst_ExceptionSafety: public QObject
{
Q_OBJECT
private slots:
#ifdef QT_NO_EXCEPTIONS
void initTestCase();
#else
void exceptionInSlot();
void exceptionVector();
void exceptionHash();
void exceptionMap();
void exceptionList();
void exceptionLinkedList();
// void exceptionEventLoop();
// void exceptionSignalSlot();
void exceptionOOMQVarLengthArray();
#endif
};
#ifdef QT_NO_EXCEPTIONS
void tst_ExceptionSafety::initTestCase()
{
QSKIP("This test requires exception support");
}
#else
class Emitter : public QObject
{
Q_OBJECT
public:
inline void emitTestSignal() { emit testSignal(); }
signals:
void testSignal();
};
class ExceptionThrower : public QObject
{
Q_OBJECT
public slots:
void thrower() { throw 5; }
};
class Receiver : public QObject
{
Q_OBJECT
public:
Receiver()
: received(0) {}
int received;
public slots:
void receiver() { ++received; }
};
enum ThrowType { ThrowNot = 0, ThrowAtCreate = 1, ThrowAtCopy = 2, ThrowLater = 3, ThrowAtComparison = 4 };
ThrowType throwType = ThrowNot; // global flag to indicate when an exception should be throw. Will be reset when the exception has been generated.
int objCounter = 0;
/*! Class that does not throw any exceptions. Used as baseclass for all the other ones.
*/
template <int T>
class FlexibleThrower
{
public:
FlexibleThrower() : _value(-1) {
if( throwType == ThrowAtCreate ) {
throwType = ThrowNot;
throw ThrowAtCreate;
}
objCounter++;
}
FlexibleThrower( short value ) : _value(value) {
if( throwType == ThrowAtCreate ) {
throwType = ThrowNot;
throw ThrowAtCreate;
}
objCounter++;
}
FlexibleThrower(FlexibleThrower const& other ) {
// qDebug("cc");
if( throwType == ThrowAtCopy ) {
throwType = ThrowNot;
throw ThrowAtCopy;
} else if( throwType == ThrowLater ) {
throwType = ThrowAtCopy;
}
objCounter++;
_value = other.value();
}
~FlexibleThrower() { objCounter--; }
bool operator==(const FlexibleThrower<T> &t) const
{
// qDebug("vv == %d %d", value(), t.value());
if( throwType == ThrowAtComparison ) {
throwType = ThrowNot;
throw ThrowAtComparison;
}
return value()==t.value();
}
bool operator<(const FlexibleThrower<T> &t) const
{
// qDebug("vv < %d %d", value(), t.value());
if( throwType == ThrowAtComparison ) {
throwType = ThrowNot;
throw ThrowAtComparison;
}
return value()<t.value();
}
int value() const
{ return (int)_value; }
short _value;
char dummy[T];
};
uint qHash(const FlexibleThrower<2>& t)
{
// qDebug("ha");
if( throwType == ThrowAtComparison ) {
throwType = ThrowNot;
throw ThrowAtComparison;
}
return (uint)t.value();
}
typedef FlexibleThrower<2> FlexibleThrowerSmall;
typedef QMap<FlexibleThrowerSmall,FlexibleThrowerSmall> MyMap;
typedef QHash<FlexibleThrowerSmall,FlexibleThrowerSmall> MyHash;
// connect a signal to a slot that throws an exception
// run this through valgrind to make sure it doesn't corrupt
void tst_ExceptionSafety::exceptionInSlot()
{
Emitter emitter;
ExceptionThrower thrower;
connect(&emitter, SIGNAL(testSignal()), &thrower, SLOT(thrower()));
try {
emitter.emitTestSignal();
} catch (int i) {
QCOMPARE(i, 5);
}
}
void tst_ExceptionSafety::exceptionList()
{
const int intancesCount = objCounter;
{
int instances;
QList<FlexibleThrowerSmall> list;
QList<FlexibleThrowerSmall> list2;
QList<FlexibleThrowerSmall> list3;
for( int i = 0; i<10; i++ )
list.append( FlexibleThrowerSmall(i) );
instances = objCounter;
try {
throwType = ThrowAtCopy;
list.append( FlexibleThrowerSmall(10));
} catch (...) {
QCOMPARE(instances, objCounter);
}
QCOMPARE( list.size(), 10 );
instances = objCounter;
try {
throwType = ThrowAtCopy;
list.prepend( FlexibleThrowerSmall(10));
} catch (...) {
QCOMPARE(instances, objCounter);
}
QCOMPARE( list.at(0).value(), 0 );
QCOMPARE( list.size(), 10 );
instances = objCounter;
try {
throwType = ThrowAtCopy;
list.insert( 8, FlexibleThrowerSmall(10));
} catch (...) {
QCOMPARE(instances, objCounter);
}
QCOMPARE( list.at(7).value(), 7 );
QCOMPARE( list.at(8).value(), 8 );
QCOMPARE( list.size(), 10 );
instances = objCounter;
try {
throwType = ThrowAtCopy;
FlexibleThrowerSmall t = list.takeAt( 6 );
} catch (...) {
QCOMPARE(instances, objCounter);
}
QCOMPARE( list.at(6).value(), 6 );
QCOMPARE( list.at(7).value(), 7 );
QCOMPARE( list.size(), 10 );
instances = objCounter;
try {
throwType = ThrowAtCopy;
list3 = list;
} catch (...) {
QCOMPARE(instances, objCounter);
}
QCOMPARE( list.at(0).value(), 0 );
QCOMPARE( list.at(7).value(), 7 );
QCOMPARE( list.size(), 10 );
QCOMPARE( list3.at(0).value(), 0 );
QCOMPARE( list3.at(7).value(), 7 );
QCOMPARE( list3.size(), 10 );
instances = objCounter;
try {
throwType = ThrowAtCopy;
list3.append( FlexibleThrowerSmall(11) );
} catch (...) {
QCOMPARE(instances, objCounter);
}
QCOMPARE( list.at(0).value(), 0 );
QCOMPARE( list.at(7).value(), 7 );
QCOMPARE( list.size(), 10 );
QCOMPARE( list3.at(0).value(), 0 );
QCOMPARE( list3.at(7).value(), 7 );
QCOMPARE( list3.size(), 10 );
try {
list2.clear();
list2.append( FlexibleThrowerSmall(11));
throwType = ThrowAtCopy;
instances = objCounter;
list3 = list+list2;
} catch (...) {
QCOMPARE(instances, objCounter);
}
QCOMPARE( list.at(0).value(), 0 );
QCOMPARE( list.at(7).value(), 7 );
QCOMPARE( list.size(), 10 );
// check that copy on write works atomar
list2.clear();
list2.append( FlexibleThrowerSmall(11));
list3 = list+list2;
instances = objCounter;
try {
throwType = ThrowAtCreate;
list3[7]=FlexibleThrowerSmall(12);
} catch (...) {
QCOMPARE(instances, objCounter);
}
QCOMPARE( list.at(7).value(), 7 );
QCOMPARE( list.size(), 10 );
QCOMPARE( list3.at(7).value(), 7 );
QCOMPARE( list3.size(), 11 );
}
QCOMPARE(objCounter, intancesCount); // check that every object has been freed
}
void tst_ExceptionSafety::exceptionLinkedList()
{
const int intancesCount = objCounter;
{
int instances;
QLinkedList<FlexibleThrowerSmall> list;
QLinkedList<FlexibleThrowerSmall> list2;
QLinkedList<FlexibleThrowerSmall> list3;
for( int i = 0; i<10; i++ )
list.append( FlexibleThrowerSmall(i) );
instances = objCounter;
try {
throwType = ThrowAtCopy;
list.append( FlexibleThrowerSmall(10));
} catch (...) {
QCOMPARE(instances, objCounter);
}
QCOMPARE( list.size(), 10 );
instances = objCounter;
try {
throwType = ThrowAtCopy;
list.prepend( FlexibleThrowerSmall(10));
} catch (...) {
QCOMPARE(instances, objCounter);
}
QCOMPARE( list.first().value(), 0 );
QCOMPARE( list.size(), 10 );
instances = objCounter;
try {
throwType = ThrowAtCopy;
list3 = list;
list3.append( FlexibleThrowerSmall(11) );
} catch (...) {
QCOMPARE(instances, objCounter);
}
QCOMPARE( list.first().value(), 0 );
QCOMPARE( list.size(), 10 );
QCOMPARE( list3.size(), 10 );
}
QCOMPARE(objCounter, intancesCount); // check that every object has been freed
}
void tst_ExceptionSafety::exceptionVector()
{
const int intancesCount = objCounter;
{
int instances;
QVector<FlexibleThrowerSmall> vector;
QVector<FlexibleThrowerSmall> vector2;
QVector<FlexibleThrowerSmall> vector3;
for (int i = 0; i<10; i++)
vector.append( FlexibleThrowerSmall(i) );
instances = objCounter;
try {
throwType = ThrowAtCopy;
vector.append( FlexibleThrowerSmall(10));
} catch (...) {
QCOMPARE(instances, objCounter);
}
QCOMPARE( vector.size(), 10 );
instances = objCounter;
try {
throwType = ThrowAtCopy;
vector.prepend( FlexibleThrowerSmall(10));
} catch (...) {
QCOMPARE(instances, objCounter);
}
QCOMPARE( vector.at(0).value(), 0 );
QCOMPARE( vector.size(), 10 );
instances = objCounter;
try {
throwType = ThrowAtCopy;
vector.insert( 8, FlexibleThrowerSmall(10));
} catch (...) {
QCOMPARE(instances, objCounter);
}
QCOMPARE( vector.at(7).value(), 7 );
QCOMPARE( vector.at(8).value(), 8 );
QCOMPARE( vector.size(), 10 );
instances = objCounter;
try {
throwType = ThrowAtCopy;
vector3 = vector;
} catch (...) {
QCOMPARE(instances, objCounter);
}
QCOMPARE( vector.at(0).value(), 0 );
QCOMPARE( vector.at(7).value(), 7 );
QCOMPARE( vector.size(), 10 );
QCOMPARE( vector3.at(0).value(), 0 );
QCOMPARE( vector3.at(7).value(), 7 );
QCOMPARE( vector3.size(), 10 );
instances = objCounter;
try {
throwType = ThrowAtCopy;
vector3.append( FlexibleThrowerSmall(11) );
} catch (...) {
QCOMPARE(instances, objCounter);
}
QCOMPARE( vector.at(0).value(), 0 );
QCOMPARE( vector.at(7).value(), 7 );
QCOMPARE( vector.size(), 10 );
QCOMPARE( vector3.at(0).value(), 0 );
QCOMPARE( vector3.at(7).value(), 7 );
try {
vector2.clear();
vector2.append( FlexibleThrowerSmall(11));
instances = objCounter;
throwType = ThrowAtCopy;
vector3 = vector+vector2;
} catch (...) {
QCOMPARE(instances, objCounter);
}
QCOMPARE( vector.at(0).value(), 0 );
QCOMPARE( vector.at(7).value(), 7 );
QCOMPARE( vector.size(), 10 );
// check that copy on write works atomar
vector2.clear();
vector2.append( FlexibleThrowerSmall(11));
vector3 = vector+vector2;
instances = objCounter;
try {
throwType = ThrowAtCreate;
vector3[7]=FlexibleThrowerSmall(12);
} catch (...) {
QCOMPARE(instances, objCounter);
}
QCOMPARE( vector.at(7).value(), 7 );
QCOMPARE( vector.size(), 10 );
QCOMPARE( vector3.at(7).value(), 7 );
QCOMPARE( vector3.size(), 11 );
instances = objCounter;
try {
throwType = ThrowAtCreate;
vector.resize(15);
} catch (...) {
QCOMPARE(instances, objCounter);
}
QCOMPARE( vector.at(7).value(), 7 );
QCOMPARE( vector.size(), 10 );
instances = objCounter;
try {
throwType = ThrowAtCreate;
vector.resize(15);
} catch (...) {
QCOMPARE(instances, objCounter);
}
QCOMPARE( vector.at(7).value(), 7 );
QCOMPARE( vector.size(), 10 );
instances = objCounter;
try {
throwType = ThrowLater;
vector.fill(FlexibleThrowerSmall(1), 15);
} catch (...) {
QCOMPARE(instances, objCounter);
}
QCOMPARE( vector.at(0).value(), 0 );
QCOMPARE( vector.size(), 10 );
}
QCOMPARE(objCounter, intancesCount); // check that every object has been freed
}
void tst_ExceptionSafety::exceptionMap()
{
const int intancesCount = objCounter;
{
int instances;
MyMap map;
MyMap map2;
MyMap map3;
throwType = ThrowNot;
for (int i = 0; i<10; i++)
map[ FlexibleThrowerSmall(i) ] = FlexibleThrowerSmall(i);
return; // further test are deactivated until Map is fixed.
for( int i = ThrowAtCopy; i<=ThrowAtComparison; i++ ) {
instances = objCounter;
try {
throwType = (ThrowType)i;
map[ FlexibleThrowerSmall(10) ] = FlexibleThrowerSmall(10);
} catch(...) {
QCOMPARE(instances, objCounter);
}
QCOMPARE( map.size(), 10 );
QCOMPARE( map[ FlexibleThrowerSmall(1) ], FlexibleThrowerSmall(1) );
}
map2 = map;
instances = objCounter;
try {
throwType = ThrowLater;
map2[ FlexibleThrowerSmall(10) ] = FlexibleThrowerSmall(10);
} catch(...) {
QCOMPARE(instances, objCounter);
}
/* qDebug("%d %d", map.size(), map2.size() );
for( int i=0; i<map.size(); i++ )
qDebug( "Value at %d: %d",i, map.value(FlexibleThrowerSmall(i), FlexibleThrowerSmall()).value() );
QCOMPARE( map.value(FlexibleThrowerSmall(1), FlexibleThrowerSmall()), FlexibleThrowerSmall(1) );
qDebug( "Value at %d: %d",1, map[FlexibleThrowerSmall(1)].value() );
qDebug("%d %d", map.size(), map2.size() );
*/
QCOMPARE( map[ FlexibleThrowerSmall(1) ], FlexibleThrowerSmall(1) );
QCOMPARE( map.size(), 10 );
QCOMPARE( map2[ FlexibleThrowerSmall(1) ], FlexibleThrowerSmall(1) );
QCOMPARE( map2.size(), 10 );
}
QCOMPARE(objCounter, intancesCount); // check that every object has been freed
}
void tst_ExceptionSafety::exceptionHash()
{
const int intancesCount = objCounter;
{
int instances;
MyHash hash;
MyHash hash2;
MyHash hash3;
for( int i = 0; i<10; i++ )
hash[ FlexibleThrowerSmall(i) ] = FlexibleThrowerSmall(i);
for( int i = ThrowAtCopy; i<=ThrowAtComparison; i++ ) {
instances = objCounter;
try {
throwType = (ThrowType)i;
hash[ FlexibleThrowerSmall(10) ] = FlexibleThrowerSmall(10);
} catch(...) {
QCOMPARE(instances, objCounter);
}
QCOMPARE( hash.size(), 10 );
}
hash2 = hash;
instances = objCounter;
try {
throwType = ThrowLater;
hash2[ FlexibleThrowerSmall(10) ] = FlexibleThrowerSmall(10);
} catch(...) {
QCOMPARE(instances, objCounter);
}
QCOMPARE( hash[ FlexibleThrowerSmall(1) ], FlexibleThrowerSmall(1) );
QCOMPARE( hash.size(), 10 );
QCOMPARE( hash2[ FlexibleThrowerSmall(1) ], FlexibleThrowerSmall(1) );
QCOMPARE( hash2.size(), 10 );
hash2.clear();
instances = objCounter;
try {
throwType = ThrowLater;
hash2.reserve(30);
} catch(...) {
QCOMPARE(instances, objCounter);
}
QCOMPARE( hash2.size(), 0 );
/*
try {
throwType = ThrowAtCopy;
hash.prepend( FlexibleThrowerSmall(10));
} catch (...) {
}
QCOMPARE( hash.at(0).value(), 0 );
QCOMPARE( hash.size(), 10 );
try {
throwType = ThrowAtCopy;
hash.insert( 8, FlexibleThrowerSmall(10));
} catch (...) {
}
QCOMPARE( hash.at(7).value(), 7 );
QCOMPARE( hash.at(8).value(), 8 );
QCOMPARE( hash.size(), 10 );
qDebug("val");
try {
throwType = ThrowAtCopy;
hash3 = hash;
} catch (...) {
}
QCOMPARE( hash.at(0).value(), 0 );
QCOMPARE( hash.at(7).value(), 7 );
QCOMPARE( hash.size(), 10 );
QCOMPARE( hash3.at(0).value(), 0 );
QCOMPARE( hash3.at(7).value(), 7 );
QCOMPARE( hash3.size(), 10 );
try {
throwType = ThrowAtCopy;
hash3.append( FlexibleThrowerSmall(11) );
} catch (...) {
}
QCOMPARE( hash.at(0).value(), 0 );
QCOMPARE( hash.at(7).value(), 7 );
QCOMPARE( hash.size(), 10 );
QCOMPARE( hash3.at(0).value(), 0 );
QCOMPARE( hash3.at(7).value(), 7 );
QCOMPARE( hash3.at(11).value(), 11 );
try {
hash2.clear();
hash2.append( FlexibleThrowerSmall(11));
throwType = ThrowAtCopy;
hash3 = hash+hash2;
} catch (...) {
}
QCOMPARE( hash.at(0).value(), 0 );
QCOMPARE( hash.at(7).value(), 7 );
QCOMPARE( hash.size(), 10 );
// check that copy on write works atomar
hash2.clear();
hash2.append( FlexibleThrowerSmall(11));
hash3 = hash+hash2;
try {
throwType = ThrowAtCopy;
hash3[7]=FlexibleThrowerSmall(12);
} catch (...) {
}
QCOMPARE( hash.at(7).value(), 7 );
QCOMPARE( hash.size(), 10 );
QCOMPARE( hash3.at(7).value(), 7 );
QCOMPARE( hash3.size(), 11 );
*/
}
QCOMPARE(objCounter, intancesCount); // check that every object has been freed
}
// Disable these tests until the level of exception safety in event loops is clear
#if 0
enum
{
ThrowEventId = QEvent::User + 42,
NoThrowEventId = QEvent::User + 43
};
class ThrowEvent : public QEvent
{
public:
ThrowEvent()
: QEvent(static_cast<QEvent::Type>(ThrowEventId))
{
}
};
class NoThrowEvent : public QEvent
{
public:
NoThrowEvent()
: QEvent(static_cast<QEvent::Type>(NoThrowEventId))
{}
};
struct IntEx : public std::exception
{
IntEx(int aEx) : ex(aEx) {}
int ex;
};
class TestObject : public QObject
{
public:
TestObject()
: throwEventCount(0), noThrowEventCount(0) {}
int throwEventCount;
int noThrowEventCount;
protected:
bool event(QEvent *event)
{
if (int(event->type()) == ThrowEventId) {
throw IntEx(++throwEventCount);
} else if (int(event->type()) == NoThrowEventId) {
++noThrowEventCount;
}
return QObject::event(event);
}
};
void tst_ExceptionSafety::exceptionEventLoop()
{
// send an event that throws
TestObject obj;
ThrowEvent throwEvent;
try {
qApp->sendEvent(&obj, &throwEvent);
} catch (IntEx code) {
QCOMPARE(code.ex, 1);
}
QCOMPARE(obj.throwEventCount, 1);
// post an event that throws
qApp->postEvent(&obj, new ThrowEvent);
try {
qApp->processEvents();
} catch (IntEx code) {
QCOMPARE(code.ex, 2);
}
QCOMPARE(obj.throwEventCount, 2);
// post a normal event, then a throwing event, then a normal event
// run this in valgrind to ensure that it doesn't leak.
qApp->postEvent(&obj, new NoThrowEvent);
qApp->postEvent(&obj, new ThrowEvent);
qApp->postEvent(&obj, new NoThrowEvent);
try {
qApp->processEvents();
} catch (IntEx code) {
QCOMPARE(code.ex, 3);
}
// here, we should have received on non-throwing event and one throwing one
QCOMPARE(obj.throwEventCount, 3);
QCOMPARE(obj.noThrowEventCount, 1);
// spin the event loop again
qApp->processEvents();
// now, we should have received the second non-throwing event
QCOMPARE(obj.noThrowEventCount, 2);
}
void tst_ExceptionSafety::exceptionSignalSlot()
{
Emitter e;
ExceptionThrower thrower;
Receiver r1;
Receiver r2;
// connect a signal to a normal object, a thrower and a normal object again
connect(&e, SIGNAL(testSignal()), &r1, SLOT(receiver()));
connect(&e, SIGNAL(testSignal()), &thrower, SLOT(thrower()));
connect(&e, SIGNAL(testSignal()), &r2, SLOT(receiver()));
int code = 0;
try {
e.emitTestSignal();
} catch (int c) {
code = c;
}
// 5 is the magic number that's thrown by thrower
QCOMPARE(code, 5);
// assumption: slots are called in the connection order
QCOMPARE(r1.received, 1);
QCOMPARE(r2.received, 0);
}
#endif
static bool outOfMemory = false;
void* internalMalloc(size_t bytes) { return outOfMemory ? 0 : malloc(bytes); }
struct OutOfMemory
{
OutOfMemory() { outOfMemory = true; }
~OutOfMemory() { outOfMemory = false; }
};
void tst_ExceptionSafety::exceptionOOMQVarLengthArray()
{
#ifdef QT_NO_EXCEPTIONS
// it will crash by design
Q_STATIC_ASSERT(false);
#else
QVarLengthArray<char> arr0;
int minSize = arr0.capacity();
// constructor throws
bool success = false;
try {
OutOfMemory oom;
QVarLengthArray<char> arr(minSize * 2);
} catch (const std::bad_alloc&) {
success = true;
}
QVERIFY(success);
QVarLengthArray<char> arr;
// resize throws
success = false;
try {
OutOfMemory oom;
arr.resize(minSize * 2);
} catch(const std::bad_alloc&) {
arr.resize(1);
success = true;
}
QVERIFY(success);
#endif
}
#endif
QTEST_MAIN(tst_ExceptionSafety)
#include "tst_exceptionsafety.moc"

View File

@ -1,319 +0,0 @@
/*
----------------------------------------------------------------
Notice that the following BSD-style license applies to this one
file (memcheck.h) only. The rest of Valgrind is licensed under the
terms of the GNU General Public License, version 2, unless
otherwise indicated. See the COPYING file in the source
distribution for details.
----------------------------------------------------------------
This file is part of MemCheck, a heavyweight Valgrind tool for
detecting memory errors.
Copyright (C) 2000-2008 Julian Seward. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. The origin of this software must not be misrepresented; you must
not claim that you wrote the original software. If you use this
software in a product, an acknowledgment in the product
documentation would be appreciated but is not required.
3. Altered source versions must be plainly marked as such, and must
not be misrepresented as being the original software.
4. The name of the author may not be used to endorse or promote
products derived from this software without specific prior written
permission.
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
----------------------------------------------------------------
Notice that the above BSD-style license applies to this one file
(memcheck.h) only. The entire rest of Valgrind is licensed under
the terms of the GNU General Public License, version 2. See the
COPYING file in the source distribution for details.
----------------------------------------------------------------
*/
#ifndef __MEMCHECK_H
#define __MEMCHECK_H
/* This file is for inclusion into client (your!) code.
You can use these macros to manipulate and query memory permissions
inside your own programs.
See comment near the top of valgrind.h on how to use them.
*/
#include "valgrind.h"
/* !! ABIWARNING !! ABIWARNING !! ABIWARNING !! ABIWARNING !!
This enum comprises an ABI exported by Valgrind to programs
which use client requests. DO NOT CHANGE THE ORDER OF THESE
ENTRIES, NOR DELETE ANY -- add new ones at the end. */
typedef
enum {
VG_USERREQ__MAKE_MEM_NOACCESS = VG_USERREQ_TOOL_BASE('M','C'),
VG_USERREQ__MAKE_MEM_UNDEFINED,
VG_USERREQ__MAKE_MEM_DEFINED,
VG_USERREQ__DISCARD,
VG_USERREQ__CHECK_MEM_IS_ADDRESSABLE,
VG_USERREQ__CHECK_MEM_IS_DEFINED,
VG_USERREQ__DO_LEAK_CHECK,
VG_USERREQ__COUNT_LEAKS,
VG_USERREQ__GET_VBITS,
VG_USERREQ__SET_VBITS,
VG_USERREQ__CREATE_BLOCK,
VG_USERREQ__MAKE_MEM_DEFINED_IF_ADDRESSABLE,
VG_USERREQ__ENABLE_OOM,
VG_USERREQ__GET_ALLOC_INDEX,
/* This is just for memcheck's internal use - don't use it */
_VG_USERREQ__MEMCHECK_RECORD_OVERLAP_ERROR
= VG_USERREQ_TOOL_BASE('M','C') + 256,
/* This is just for memcheck's internal use - don't use it */
_VG_USERREQ__EXCEPTION
= VG_USERREQ_TOOL_BASE('M','C') + 512,
} Vg_MemCheckClientRequest;
/* Client-code macros to manipulate the state of memory. */
/* Mark memory at _qzz_addr as unaddressable for _qzz_len bytes. */
#define VALGRIND_MAKE_MEM_NOACCESS(_qzz_addr,_qzz_len) \
(__extension__({unsigned long _qzz_res; \
VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0 /* default return */, \
VG_USERREQ__MAKE_MEM_NOACCESS, \
_qzz_addr, _qzz_len, 0, 0, 0); \
_qzz_res; \
}))
/* Similarly, mark memory at _qzz_addr as addressable but undefined
for _qzz_len bytes. */
#define VALGRIND_MAKE_MEM_UNDEFINED(_qzz_addr,_qzz_len) \
(__extension__({unsigned long _qzz_res; \
VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0 /* default return */, \
VG_USERREQ__MAKE_MEM_UNDEFINED, \
_qzz_addr, _qzz_len, 0, 0, 0); \
_qzz_res; \
}))
/* Similarly, mark memory at _qzz_addr as addressable and defined
for _qzz_len bytes. */
#define VALGRIND_MAKE_MEM_DEFINED(_qzz_addr,_qzz_len) \
(__extension__({unsigned long _qzz_res; \
VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0 /* default return */, \
VG_USERREQ__MAKE_MEM_DEFINED, \
_qzz_addr, _qzz_len, 0, 0, 0); \
_qzz_res; \
}))
/* Similar to VALGRIND_MAKE_MEM_DEFINED except that addressability is
not altered: bytes which are addressable are marked as defined,
but those which are not addressable are left unchanged. */
#define VALGRIND_MAKE_MEM_DEFINED_IF_ADDRESSABLE(_qzz_addr,_qzz_len) \
(__extension__({unsigned long _qzz_res; \
VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0 /* default return */, \
VG_USERREQ__MAKE_MEM_DEFINED_IF_ADDRESSABLE, \
_qzz_addr, _qzz_len, 0, 0, 0); \
_qzz_res; \
}))
/* Create a block-description handle. The description is an ascii
string which is included in any messages pertaining to addresses
within the specified memory range. Has no other effect on the
properties of the memory range. */
#define VALGRIND_CREATE_BLOCK(_qzz_addr,_qzz_len, _qzz_desc) \
(__extension__({unsigned long _qzz_res; \
VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0 /* default return */, \
VG_USERREQ__CREATE_BLOCK, \
_qzz_addr, _qzz_len, _qzz_desc, \
0, 0); \
_qzz_res; \
}))
/* Discard a block-description-handle. Returns 1 for an
invalid handle, 0 for a valid handle. */
#define VALGRIND_DISCARD(_qzz_blkindex) \
(__extension__ ({unsigned long _qzz_res; \
VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0 /* default return */, \
VG_USERREQ__DISCARD, \
0, _qzz_blkindex, 0, 0, 0); \
_qzz_res; \
}))
/* Client-code macros to check the state of memory. */
/* Check that memory at _qzz_addr is addressable for _qzz_len bytes.
If suitable addressibility is not established, Valgrind prints an
error message and returns the address of the first offending byte.
Otherwise it returns zero. */
#define VALGRIND_CHECK_MEM_IS_ADDRESSABLE(_qzz_addr,_qzz_len) \
(__extension__({unsigned long _qzz_res; \
VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \
VG_USERREQ__CHECK_MEM_IS_ADDRESSABLE,\
_qzz_addr, _qzz_len, 0, 0, 0); \
_qzz_res; \
}))
/* Check that memory at _qzz_addr is addressable and defined for
_qzz_len bytes. If suitable addressibility and definedness are not
established, Valgrind prints an error message and returns the
address of the first offending byte. Otherwise it returns zero. */
#define VALGRIND_CHECK_MEM_IS_DEFINED(_qzz_addr,_qzz_len) \
(__extension__({unsigned long _qzz_res; \
VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \
VG_USERREQ__CHECK_MEM_IS_DEFINED, \
_qzz_addr, _qzz_len, 0, 0, 0); \
_qzz_res; \
}))
/* Use this macro to force the definedness and addressibility of an
lvalue to be checked. If suitable addressibility and definedness
are not established, Valgrind prints an error message and returns
the address of the first offending byte. Otherwise it returns
zero. */
#define VALGRIND_CHECK_VALUE_IS_DEFINED(__lvalue) \
VALGRIND_CHECK_MEM_IS_DEFINED( \
(volatile unsigned char *)&(__lvalue), \
(unsigned long)(sizeof (__lvalue)))
/* Do a memory leak check mid-execution. */
#define VALGRIND_DO_LEAK_CHECK \
{unsigned long _qzz_res; \
VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \
VG_USERREQ__DO_LEAK_CHECK, \
0, 0, 0, 0, 0); \
}
/* Just display summaries of leaked memory, rather than all the
details */
#define VALGRIND_DO_QUICK_LEAK_CHECK \
{unsigned long _qzz_res; \
VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \
VG_USERREQ__DO_LEAK_CHECK, \
1, 0, 0, 0, 0); \
}
/* Return number of leaked, dubious, reachable and suppressed bytes found by
all previous leak checks. They must be lvalues. */
#define VALGRIND_COUNT_LEAKS(leaked, dubious, reachable, suppressed) \
/* For safety on 64-bit platforms we assign the results to private
unsigned long variables, then assign these to the lvalues the user
specified, which works no matter what type 'leaked', 'dubious', etc
are. We also initialise '_qzz_leaked', etc because
VG_USERREQ__COUNT_LEAKS doesn't mark the values returned as
initialised. */ \
{unsigned long _qzz_res; \
unsigned long _qzz_leaked = 0, _qzz_dubious = 0; \
unsigned long _qzz_reachable = 0, _qzz_suppressed = 0; \
VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \
VG_USERREQ__COUNT_LEAKS, \
&_qzz_leaked, &_qzz_dubious, \
&_qzz_reachable, &_qzz_suppressed, 0); \
leaked = _qzz_leaked; \
dubious = _qzz_dubious; \
reachable = _qzz_reachable; \
suppressed = _qzz_suppressed; \
}
/* Get the validity data for addresses [zza..zza+zznbytes-1] and copy it
into the provided zzvbits array. Return values:
0 if not running on valgrind
1 success
2 [previously indicated unaligned arrays; these are now allowed]
3 if any parts of zzsrc/zzvbits are not addressable.
The metadata is not copied in cases 0, 2 or 3 so it should be
impossible to segfault your system by using this call.
*/
#define VALGRIND_GET_VBITS(zza,zzvbits,zznbytes) \
(__extension__({unsigned long _qzz_res; \
char* czza = (char*)zza; \
char* czzvbits = (char*)zzvbits; \
VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \
VG_USERREQ__GET_VBITS, \
czza, czzvbits, zznbytes, 0, 0 ); \
_qzz_res; \
}))
/* Set the validity data for addresses [zza..zza+zznbytes-1], copying it
from the provided zzvbits array. Return values:
0 if not running on valgrind
1 success
2 [previously indicated unaligned arrays; these are now allowed]
3 if any parts of zza/zzvbits are not addressable.
The metadata is not copied in cases 0, 2 or 3 so it should be
impossible to segfault your system by using this call.
*/
#define VALGRIND_SET_VBITS(zza,zzvbits,zznbytes) \
(__extension__({unsigned int _qzz_res; \
char* czza = (char*)zza; \
char* czzvbits = (char*)zzvbits; \
VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \
VG_USERREQ__SET_VBITS, \
czza, czzvbits, zznbytes, 0, 0 ); \
_qzz_res; \
}))
/* Enable or disable OOM simulation. */
#define VALGRIND_ENABLE_OOM_AT_ALLOC_INDEX(index) \
(__extension__ ({unsigned long _qzz_res; \
VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0 /* default return */, \
VG_USERREQ__ENABLE_OOM, \
1, index, 0, 0, 0); \
_qzz_res; \
}))
#define VALGRIND_DISABLE_OOM_AT_ALLOC_INDEX(index) \
(__extension__ ({unsigned long _qzz_res; \
VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0 /* default return */, \
VG_USERREQ__ENABLE_OOM, \
0, index, 0, 0, 0); \
_qzz_res; \
}))
/* Get the current allocation index. */
#define VALGRIND_GET_ALLOC_INDEX \
(__extension__ ({unsigned long _qzz_res; \
VALGRIND_DO_CLIENT_REQUEST(_qzz_res, -1 /* default return */, \
VG_USERREQ__GET_ALLOC_INDEX, \
0, 0, 0, 0, 0); \
_qzz_res; \
}))
#endif

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +0,0 @@
CONFIG += testcase
TARGET = tst_exceptionsafety_objects
QT += widgets testlib
HEADERS += oomsimulator.h 3rdparty/valgrind.h 3rdparty/memcheck.h
SOURCES += tst_exceptionsafety_objects.cpp
DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0

View File

@ -1,293 +0,0 @@
/****************************************************************************
**
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of the test suite of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia. For licensing terms and
** conditions see http://qt.digia.com/licensing. For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights. These rights are described in the Digia 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.
**
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include <malloc.h>
#include <limits.h>
#include <stdio.h>
#include <exception>
#if !defined(Q_OS_WIN)
# include "3rdparty/memcheck.h"
#endif
static bool mallocFailActive = false;
static int mallocFailIndex = 0;
static int mallocCount = 0;
static void my_terminate_handler()
{
// set a breakpoint here to get a backtrace for your uncaught exceptions
fprintf(stderr, "Uncaught Exception Detected. Set a breakpoint in my_terminate_handler()\n");
exit(1);
}
#if defined(__GLIBC__) && !defined(__UCLIBC__)
/* Use glibc's memory allocation hooks */
// From glibc 2.14, the malloc hook variables are declared volatile.
// Note: The malloc hook implementation is marked as deprecated.
#if !defined(__MALLOC_HOOK_VOLATILE)
# define __MALLOC_HOOK_VOLATILE
#endif
/* our hooks */
static void *my_malloc_hook(size_t, const void *);
static void *my_realloc_hook(void *, size_t, const void *);
static void *my_memalign_hook(size_t, size_t, const void *);
static void my_free_hook(void *, const void *);
/* original hooks. */
static void *(*__MALLOC_HOOK_VOLATILE old_malloc_hook)(size_t, const void *);
static void *(*__MALLOC_HOOK_VOLATILE old_realloc_hook)(void *, size_t, const void *);
static void *(*__MALLOC_HOOK_VOLATILE old_memalign_hook)(size_t, size_t, const void *);
static void (*__MALLOC_HOOK_VOLATILE old_free_hook)(void *, const void *);
/* initializer function */
static void my_init_hook();
/* Override initialising hook from the C library. */
void (*__MALLOC_HOOK_VOLATILE __malloc_initialize_hook) (void) = my_init_hook;
static void disableHooks()
{
__malloc_hook = old_malloc_hook;
__realloc_hook = old_realloc_hook;
__memalign_hook = old_memalign_hook;
__free_hook = old_free_hook;
}
static void enableHooks()
{
__malloc_hook = my_malloc_hook;
__realloc_hook = my_realloc_hook;
__memalign_hook = my_memalign_hook;
__free_hook = my_free_hook;
}
void my_init_hook()
{
old_malloc_hook = __malloc_hook;
old_realloc_hook = __realloc_hook;
old_memalign_hook = __memalign_hook;
old_free_hook = __free_hook;
enableHooks();
}
void *my_malloc_hook(size_t size, const void *)
{
++mallocCount;
if (mallocFailActive && --mallocFailIndex < 0)
return 0; // simulate OOM
__malloc_hook = old_malloc_hook;
void *result = ::malloc (size);
__malloc_hook = my_malloc_hook;
return result;
}
void *my_memalign_hook(size_t alignment, size_t size, const void *)
{
++mallocCount;
if (mallocFailActive && --mallocFailIndex < 0)
return 0; // simulate OOM
__memalign_hook = old_memalign_hook;
void *result = ::memalign(alignment, size);
__memalign_hook = my_memalign_hook;
return result;
}
void *my_realloc_hook(void *ptr, size_t size, const void *)
{
++mallocCount;
if (mallocFailActive && --mallocFailIndex < 0)
return 0; // simulate OOM
__realloc_hook = old_realloc_hook;
__malloc_hook = old_malloc_hook;
void *result = ::realloc(ptr, size);
__malloc_hook = my_malloc_hook;
__realloc_hook = my_realloc_hook;
return result;
}
void my_free_hook(void *ptr, const void *)
{
__free_hook = old_free_hook;
::free(ptr);
__free_hook = my_free_hook;
}
#elif defined(Q_CC_MSVC)
#include "crtdbg.h"
static int qCrtAllocHook(int allocType, void * /*userData*/, size_t /*size*/,
int blockType, long /*requestNumber*/,
const unsigned char * /*filename*/, int /*lineNumber*/)
{
if (blockType == _CRT_BLOCK)
return true; // ignore allocations from the C library
switch (allocType) {
case _HOOK_ALLOC:
case _HOOK_REALLOC:
++mallocCount;
if (mallocFailActive && --mallocFailIndex < 0)
return false; // simulate OOM
}
return true;
}
static struct QCrtDebugRegistrator
{
QCrtDebugRegistrator()
{
_CrtSetAllocHook(qCrtAllocHook);
}
} crtDebugRegistrator;
#else
static void disableHooks()
{
}
#endif
struct AllocFailer
{
inline AllocFailer(int index) { reactivateAt(index); }
inline ~AllocFailer() { deactivate(); }
inline void reactivateAt(int index)
{
#ifdef RUNNING_ON_VALGRIND
if (RUNNING_ON_VALGRIND)
VALGRIND_ENABLE_OOM_AT_ALLOC_INDEX(VALGRIND_GET_ALLOC_INDEX + index + 1);
#endif
mallocFailIndex = index;
mallocFailActive = true;
}
inline void deactivate()
{
mallocFailActive = false;
#ifdef RUNNING_ON_VALGRIND
VALGRIND_ENABLE_OOM_AT_ALLOC_INDEX(0);
#endif
}
inline int currentAllocIndex() const
{
#ifdef RUNNING_ON_VALGRIND
if (RUNNING_ON_VALGRIND)
return VALGRIND_GET_ALLOC_INDEX;
#endif
return mallocCount;
}
static bool initialize()
{
std::set_terminate(my_terminate_handler);
#ifdef RUNNING_ON_VALGRIND
if (RUNNING_ON_VALGRIND) {
if (VALGRIND_GET_ALLOC_INDEX == -1u) {
qWarning("You must use a valgrind with oom simulation support");
return false;
}
// running in valgrind - don't use glibc hooks
disableHooks();
// never stop simulating OOM
VALGRIND_DISABLE_OOM_AT_ALLOC_INDEX(-1u);
}
#endif
return true;
}
};
static void *new_helper(std::size_t size)
{
void *ptr = malloc(size);
#ifndef QT_NO_EXCEPTIONS
if (!ptr)
throw std::bad_alloc();
#endif
return ptr;
}
#ifdef Q_CC_MSVC
# pragma warning(push)
# pragma warning(disable: 4290)
#endif
// overload operator new
#ifndef QT_NO_EXCEPTIONS
void* operator new(size_t size) throw (std::bad_alloc) { return new_helper(size); }
void* operator new[](size_t size) throw (std::bad_alloc) { return new_helper(size); }
#endif
void* operator new(size_t size, const std::nothrow_t&) throw() { return malloc(size); }
void* operator new[](std::size_t size, const std::nothrow_t&) throw() { return malloc(size); }
// overload operator delete
void operator delete(void *ptr) throw() { if (ptr) free(ptr); }
void operator delete[](void *ptr) throw() { if (ptr) free(ptr); }
void operator delete(void *ptr, const std::nothrow_t&) throw() { if (ptr) free(ptr); }
void operator delete[](void *ptr, const std::nothrow_t&) throw() { if (ptr) free (ptr); }
#ifdef Q_CC_MSVC
# pragma warning(pop)
#endif
// ignore placement new and placement delete - those don't allocate.

View File

@ -1,803 +0,0 @@
/****************************************************************************
**
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of the test suite of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia. For licensing terms and
** conditions see http://qt.digia.com/licensing. For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights. These rights are described in the Digia 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.
**
**
** $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
#include "oomsimulator.h"
#include "3rdparty/memcheck.h"
class tst_ExceptionSafety_Objects: public QObject
{
Q_OBJECT
public slots:
void initTestCase();
#ifndef QT_NO_EXCEPTIONS
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 QtMessageHandler testMessageHandler;
static void safeMessageHandler(QtMsgType, const QMessageLogContext&, const QString&);
#endif
};
#ifdef QT_NO_EXCEPTIONS
void tst_ExceptionSafety_Objects::initTestCase()
{
QSKIP("This test requires exception support");
}
#else
// helper structs to create an arbitrary widget
struct AbstractTester
{
virtual ~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(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;
}
};
QtMessageHandler tst_ExceptionSafety_Objects::testMessageHandler;
void tst_ExceptionSafety_Objects::safeMessageHandler(QtMsgType type, const QMessageLogContext &ctxt,
const QString &msg)
{
// this temporarily suspends OOM testing while handling a message
int currentIndex = mallocFailIndex;
AllocFailer allocFailer(0);
allocFailer.deactivate();
(*testMessageHandler)(type, ctxt, 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 = qInstallMessageHandler(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);
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()
{
qInstallMessageHandler(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");
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()
{
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);
}
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"))
QSKIP("This type of widget is not currently strongly exception safe");
if (tag == QLatin1String("QWidget"))
QSKIP("QTBUG-18927");
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");
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);
}
#endif
QTEST_MAIN(tst_ExceptionSafety_Objects)
#include "tst_exceptionsafety_objects.moc"

View File

@ -4,8 +4,6 @@ SUBDIRS=\
baselineexample \
collections \
compiler \
exceptionsafety \
# exceptionsafety_objects \ # QObjectPrivate is not safe
gestures \
headersclean \
lancelot \
@ -70,4 +68,3 @@ wince*|!contains(QT_CONFIG, accessibility): SUBDIRS -= qaccessibility
!embedded|wince*: SUBDIRS -= \
qdirectpainter
!linux*-g++*:SUBDIRS -= exceptionsafety_objects