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:
parent
fe375dc3d0
commit
82e976d52c
1
tests/auto/other/exceptionsafety/.gitignore
vendored
1
tests/auto/other/exceptionsafety/.gitignore
vendored
@ -1 +0,0 @@
|
||||
tst_exceptionsafety
|
@ -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
|
@ -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"
|
@ -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
@ -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
|
@ -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.
|
||||
|
||||
|
@ -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"
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user