1
/****************************************************************************
2
**
3
** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4
** All rights reserved.
5
** Contact: Nokia Corporation (qt-info@nokia.com)
6
**
7
** This file is part of the test suite of the Qt Toolkit.
8
**
9
** $QT_BEGIN_LICENSE:LGPL$
10
** GNU Lesser General Public License Usage
11
** This file may be used under the terms of the GNU Lesser General Public
12
** License version 2.1 as published by the Free Software Foundation and
13
** appearing in the file LICENSE.LGPL included in the packaging of this
14
** file. Please review the following information to ensure the GNU Lesser
15
** General Public License version 2.1 requirements will be met:
16
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17
**
18
** In addition, as a special exception, Nokia gives you certain additional
19
** rights. These rights are described in the Nokia Qt LGPL Exception
20
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21
**
22
** GNU General Public License Usage
23
** Alternatively, this file may be used under the terms of the GNU General
24
** Public License version 3.0 as published by the Free Software Foundation
25
** and appearing in the file LICENSE.GPL included in the packaging of this
26
** file. Please review the following information to ensure the GNU General
27
** Public License version 3.0 requirements will be met:
28
** http://www.gnu.org/copyleft/gpl.html.
29
**
30
** Other Usage
31
** Alternatively, this file may be used in accordance with the terms and
32
** conditions contained in a signed written agreement between you and Nokia.
33
**
34
**
35
**
36
**
37
**
38
** $QT_END_LICENSE$
39
**
40
****************************************************************************/
41
42
#include <QtGui/QtGui>
43
#include <QtTest/QtTest>
44
45
#include <stddef.h>
46
#include <exception>
47
48
QT_USE_NAMESPACE
49
50
// this test only works with
51
//   * GLIBC
52
//   * MSVC - only debug builds (we need the crtdbg.h helpers)
53
//   * SYMBIAN
54
#if (defined(QT_NO_EXCEPTIONS) || (!defined(__GLIBC__) && !defined(Q_CC_MSVC) && !defined(Q_OS_SYMBIAN))) && !defined(Q_MOC_RUN)
55
    QTEST_NOOP_MAIN
56
#else
57
58
#include "oomsimulator.h"
59
#if !defined(Q_OS_SYMBIAN)
60
#include "3rdparty/memcheck.h"
61
#endif
62
63
class tst_ExceptionSafetyObjects: public QObject
64
{
65
    Q_OBJECT
66
67
public slots:
68
    void initTestCase();
69
    void cleanupTestCase();
70
71
private slots:
72
    void objects_data();
73
    void objects();
74
75
    void widgets_data();
76
    void widgets();
77
78
    void vector_data();
79
    void vector();
80
81
    void list_data();
82
    void list();
83
84
    void linkedList_data();
85
    void linkedList();
86
87
private:
88
    static QtMsgHandler testMessageHandler;
89
    static void safeMessageHandler(QtMsgType, const char *);
90
};
91
92
// helper structs to create an arbitrary widget
93
struct AbstractTester
94
{
95
    virtual void operator()(QObject *parent) = 0;
96
};
97
Q_DECLARE_METATYPE(AbstractTester *)
98
99
typedef void (*TestFunction)(QObject*);
100
Q_DECLARE_METATYPE(TestFunction)
101
102
template <typename T>
103
struct ObjectCreator : public AbstractTester
104
{
105
    void operator()(QObject *)
106
    {
107
        QScopedPointer<T> ptr(new T);
108
    }
109
};
110
111
struct BitArrayCreator : public AbstractTester
112
{
113
    void operator()(QObject *)
114
    { QScopedPointer<QBitArray> bitArray(new QBitArray(100, true)); }
115
};
116
117
struct ByteArrayMatcherCreator : public AbstractTester
118
{
119
    void operator()(QObject *)
120
    { QScopedPointer<QByteArrayMatcher> ptr(new QByteArrayMatcher("ralf test",8)); }
121
};
122
123
struct CryptographicHashCreator : public AbstractTester
124
{
125
    void operator()(QObject *)
126
    {
127
        QScopedPointer<QCryptographicHash> ptr(new QCryptographicHash(QCryptographicHash::Sha1));
128
        ptr->addData("ralf test",8);
129
    }
130
};
131
132
struct DataStreamCreator : public AbstractTester
133
{
134
    void operator()(QObject *)
135
    {
136
        QScopedPointer<QByteArray> arr(new QByteArray("hallo, test"));
137
        QScopedPointer<QDataStream> ptr(new QDataStream(arr.data(), QIODevice::ReadWrite));
138
        ptr->writeBytes("ralf test",8);
139
    }
140
};
141
142
struct DirCreator : public AbstractTester
143
{
144
    void operator()(QObject *)
145
    {
146
        QDir::cleanPath("../////././");
147
        QScopedPointer<QDir> ptr(new QDir("."));
148
        while( ptr->cdUp() )
149
            ; // just going up
150
        ptr->count();
151
        ptr->exists(ptr->path());
152
153
        QStringList filters;
154
        filters << "*.cpp" << "*.cxx" << "*.cc";
155
        ptr->setNameFilters(filters);
156
    }
157
};
158
159
void tst_ExceptionSafetyObjects::objects_data()
160
{
161
    QTest::addColumn<AbstractTester *>("objectCreator");
162
163
#define NEWROW(T) QTest::newRow(#T) << static_cast<AbstractTester *>(new ObjectCreator<T >)
164
    NEWROW(QObject);
165
    NEWROW(QBuffer);
166
    NEWROW(QFile);
167
    NEWROW(QProcess);
168
    NEWROW(QSettings);
169
    NEWROW(QThread);
170
    NEWROW(QThreadPool);
171
    NEWROW(QTranslator);
172
    NEWROW(QFSFileEngine);
173
174
#define NEWROW2(T, CREATOR) QTest::newRow(#T) << static_cast<AbstractTester *>(new CREATOR)
175
    NEWROW2(QBitArray, BitArrayCreator);
176
    NEWROW2(QByteArrayMatcher, ByteArrayMatcherCreator);
177
    NEWROW2(QCryptographicHash, CryptographicHashCreator);
178
    NEWROW2(QDataStream, DataStreamCreator);
179
    NEWROW2(QDir, DirCreator);
180
181
}
182
183
// create and destructs an object, and lets each and every allocation
184
// during construction and destruction fail.
185
template <typename T>
186
static void doOOMTest(T &testFunc, QObject *parent, int start=0)
187
{
188
    int currentOOMIndex = start;
189
    bool caught = false;
190
    bool done = false;
191
192
    AllocFailer allocFailer(0);
193
    int allocCountBefore = allocFailer.currentAllocIndex();
194
195
    do {
196
        allocFailer.reactivateAt(++currentOOMIndex);
197
198
        caught = false;
199
200
        try {
201
            testFunc(parent);
202
        } catch (const std::bad_alloc &) {
203
            caught = true;
204
        } catch (const std::exception &ex) {
205
            if (strcmp(ex.what(), "autotest swallow") != 0)
206
                throw;
207
            caught = true;
208
        }
209
210
        if (!caught) {
211
            void *buf = malloc(42);
212
            if (buf) {
213
                // we got memory here - oom test is over.
214
                free(buf);
215
                done = true;
216
            }
217
        }
218
219
        // if we get a FAIL, stop executing now
220
        if (QTest::currentTestFailed())
221
            done = true;
222
223
//#define REALLY_VERBOSE
224
#ifdef REALLY_VERBOSE
225
    fprintf(stderr, " OOM Index: %d\n", currentOOMIndex);
226
#endif
227
228
229
    } while (caught || !done);
230
231
    allocFailer.deactivate();
232
233
//#define VERBOSE
234
#ifdef VERBOSE
235
    fprintf(stderr, "OOM Test done, checked allocs: %d (range %d - %d)\n", currentOOMIndex,
236
                allocCountBefore, allocFailer.currentAllocIndex());
237
#else
238
    Q_UNUSED(allocCountBefore);
239
#endif
240
}
241
242
static int alloc1Failed = 0;
243
static int alloc2Failed = 0;
244
static int alloc3Failed = 0;
245
static int alloc4Failed = 0;
246
static int malloc1Failed = 0;
247
static int malloc2Failed = 0;
248
249
// Tests that new, new[] and malloc() fail at least once during OOM testing.
250
class SelfTestObject : public QObject
251
{
252
public:
253
    SelfTestObject(QObject *parent = 0)
254
        : QObject(parent)
255
    {
256
        try { delete new int; } catch (const std::bad_alloc &) { ++alloc1Failed; throw; }
257
        try { delete [] new double[5]; } catch (const std::bad_alloc &) { ++alloc2Failed; throw ;}
258
        void *buf = malloc(42);
259
        if (buf)
260
            free(buf);
261
        else
262
            ++malloc1Failed;
263
    }
264
265
    ~SelfTestObject()
266
    {
267
        try { delete new int; } catch (const std::bad_alloc &) { ++alloc3Failed; }
268
        try { delete [] new double[5]; } catch (const std::bad_alloc &) { ++alloc4Failed; }
269
        void *buf = malloc(42);
270
        if (buf)
271
            free(buf);
272
        else
273
            ++malloc2Failed = true;
274
    }
275
};
276
277
QtMsgHandler tst_ExceptionSafetyObjects::testMessageHandler;
278
279
void tst_ExceptionSafetyObjects::safeMessageHandler(QtMsgType type, const char *msg)
280
{
281
    // this temporarily suspends OOM testing while handling a message
282
    int currentIndex = mallocFailIndex;
283
    AllocFailer allocFailer(0);
284
    allocFailer.deactivate();
285
    (*testMessageHandler)(type, msg);
286
    allocFailer.reactivateAt(currentIndex);
287
}
288
289
typedef void (*PVF)();
290
PVF defaultTerminate;
291
void debugTerminate()
292
{
293
    // you can detect uncaught exceptions with a breakpoint in here
294
    (*defaultTerminate)();
295
}
296
297
PVF defaultUnexpected;
298
void debugUnexpected()
299
{
300
    // you can detect unexpected exceptions with a breakpoint in here
301
    (*defaultUnexpected)();
302
}
303
304
void tst_ExceptionSafetyObjects::initTestCase()
305
{
306
    // set handlers for bad exception cases, you might want to step in and breakpoint the default handlers too
307
    defaultTerminate = std::set_terminate(&debugTerminate);
308
    defaultUnexpected = std::set_unexpected(&debugUnexpected);
309
    testMessageHandler = qInstallMsgHandler(safeMessageHandler);
310
311
    QVERIFY(AllocFailer::initialize());
312
313
    // sanity check whether OOM simulation works
314
    AllocFailer allocFailer(0);
315
316
    // malloc fail index is 0 -> this malloc should fail.
317
    void *buf = malloc(42);
318
    allocFailer.deactivate();
319
    QVERIFY(!buf);
320
321
    // malloc fail index is 1 - second malloc should fail.
322
    allocFailer.reactivateAt(1);
323
    buf = malloc(42);
324
    void *buf2 = malloc(42);
325
    allocFailer.deactivate();
326
327
    QVERIFY(buf);
328
    free(buf);
329
    QVERIFY(!buf2);
330
331
#ifdef Q_OS_SYMBIAN
332
    // temporary workaround for INC138398
333
    std::new_handler nh_func = std::set_new_handler(0);
334
    (void) std::set_new_handler(nh_func);
335
#endif
336
337
    ObjectCreator<SelfTestObject> *selfTest = new ObjectCreator<SelfTestObject>;
338
    doOOMTest(*selfTest, 0);
339
    delete selfTest;
340
    QCOMPARE(alloc1Failed, 1);
341
    QCOMPARE(alloc2Failed, 1);
342
    QCOMPARE(alloc3Failed, 2);
343
    QCOMPARE(alloc4Failed, 3);
344
    QCOMPARE(malloc1Failed, 1);
345
    QCOMPARE(malloc2Failed, 1);
346
}
347
348
void tst_ExceptionSafetyObjects::cleanupTestCase()
349
{
350
    qInstallMsgHandler(testMessageHandler);
351
}
352
353
void tst_ExceptionSafetyObjects::objects()
354
{
355
    QFETCH(AbstractTester *, objectCreator);
356
357
    doOOMTest(*objectCreator, 0);
358
    
359
    delete objectCreator;
360
}
361
362
template <typename T>
363
struct WidgetCreator : public AbstractTester
364
{
365
    void operator()(QObject *parent)
366
    {
367
        Q_ASSERT(!parent || parent->isWidgetType());
368
        QScopedPointer<T> ptr(parent ? new T(static_cast<QWidget *>(parent)) : new T);
369
    }
370
};
371
372
// QSizeGrip doesn't have a default constructor - always pass parent (even though it might be 0)
373
template <> struct WidgetCreator<QSizeGrip> : public AbstractTester
374
{
375
    void operator()(QObject *parent)
376
    {
377
        Q_ASSERT(!parent || parent->isWidgetType());
378
        QScopedPointer<QSizeGrip> ptr(new QSizeGrip(static_cast<QWidget *>(parent)));
379
    }
380
};
381
382
// QDesktopWidget doesn't need a parent.
383
template <> struct WidgetCreator<QDesktopWidget> : public AbstractTester
384
{
385
    void operator()(QObject *parent)
386
    {
387
        Q_ASSERT(!parent || parent->isWidgetType());
388
        QScopedPointer<QDesktopWidget> ptr(new QDesktopWidget());
389
    }
390
};
391
void tst_ExceptionSafetyObjects::widgets_data()
392
{
393
#ifdef Q_OS_SYMBIAN
394
    // Initialise the S60 rasteriser, which crashes if started while out of memory
395
    QImage image(20, 20, QImage::Format_RGB32); 
396
    QPainter p(&image); 
397
    p.drawText(0, 15, "foo"); 
398
#endif
399
400
    QTest::addColumn<AbstractTester *>("widgetCreator");
401
402
#undef NEWROW
403
#define NEWROW(T) QTest::newRow(#T) << static_cast<AbstractTester *>(new WidgetCreator<T >)
404
405
    NEWROW(QWidget);
406
407
    NEWROW(QButtonGroup);
408
    NEWROW(QDesktopWidget);
409
    NEWROW(QCheckBox);
410
    NEWROW(QComboBox);
411
    NEWROW(QCommandLinkButton);
412
    NEWROW(QDateEdit);
413
    NEWROW(QDateTimeEdit);
414
    NEWROW(QDial);
415
    NEWROW(QDoubleSpinBox);
416
    NEWROW(QFocusFrame);
417
    NEWROW(QFontComboBox);
418
    NEWROW(QFrame);
419
    NEWROW(QGroupBox);
420
    NEWROW(QLCDNumber);
421
    NEWROW(QLabel);
422
    NEWROW(QLCDNumber);
423
    NEWROW(QLineEdit);
424
    NEWROW(QMenu);
425
    NEWROW(QPlainTextEdit);
426
    NEWROW(QProgressBar);
427
    NEWROW(QPushButton);
428
    NEWROW(QRadioButton);
429
    NEWROW(QScrollArea);
430
    NEWROW(QScrollBar);
431
    NEWROW(QSizeGrip);
432
    NEWROW(QSlider);
433
    NEWROW(QSpinBox);
434
    NEWROW(QSplitter);
435
    NEWROW(QStackedWidget);
436
    NEWROW(QStatusBar);
437
    NEWROW(QTabBar);
438
    NEWROW(QTabWidget);
439
    NEWROW(QTextBrowser);
440
    NEWROW(QTextEdit);
441
    NEWROW(QTimeEdit);
442
    NEWROW(QToolBox);
443
    NEWROW(QToolButton);
444
    NEWROW(QStatusBar);
445
    NEWROW(QToolBar);
446
    NEWROW(QMenuBar);
447
    NEWROW(QMainWindow);
448
    NEWROW(QWorkspace);
449
    NEWROW(QColumnView);
450
    NEWROW(QListView);
451
    NEWROW(QListWidget);
452
    NEWROW(QTableView);
453
    NEWROW(QTableWidget);
454
    NEWROW(QTreeView);
455
    NEWROW(QTreeWidget);
456
}
457
458
void tst_ExceptionSafetyObjects::widgets()
459
{
460
    QFETCH(AbstractTester *, widgetCreator);
461
462
    doOOMTest(*widgetCreator, 0, 00000);
463
464
    QWidget parent;
465
    doOOMTest(*widgetCreator, &parent, 00000);
466
467
    delete widgetCreator;
468
469
    // if the test reaches here without crashing, we passed :)
470
    QVERIFY(true);
471
}
472
473
struct Integer
474
{
475
    Integer(int value = 42)
476
        : ptr(new int(value))
477
    {
478
        ++instanceCount;
479
    }
480
481
    Integer(const Integer &other)
482
        : ptr(new int(*other.ptr))
483
    {
484
        ++instanceCount;
485
    }
486
487
    Integer &operator=(const Integer &other)
488
    {
489
        int *newPtr = new int(*other.ptr);
490
        delete ptr;
491
        ptr = newPtr;
492
        return *this;
493
    }
494
495
    ~Integer()
496
    {
497
        --instanceCount;
498
        delete ptr;
499
    }
500
501
    int value() const
502
    {
503
        return *ptr;
504
    }
505
506
    int *ptr;
507
    static int instanceCount;
508
};
509
510
int Integer::instanceCount = 0;
511
512
struct IntegerMoveable
513
    {
514
    IntegerMoveable(int value = 42)
515
        : val(value)
516
    {
517
        delete new int;
518
        ++instanceCount;
519
    }
520
521
    IntegerMoveable(const IntegerMoveable &other)
522
        : val(other.val)
523
    {
524
        delete new int;
525
        ++instanceCount;
526
    }
527
528
    IntegerMoveable &operator=(const IntegerMoveable &other)
529
    {
530
        delete new int;
531
        val = other.val;
532
        return *this;
533
    }
534
535
    ~IntegerMoveable()
536
    {
537
        --instanceCount;
538
    }
539
540
    int value() const
541
    {
542
        return val;
543
    }
544
545
    int val;
546
    static int instanceCount;
547
    };
548
549
int IntegerMoveable::instanceCount = 0;
550
Q_DECLARE_TYPEINFO(IntegerMoveable, Q_MOVABLE_TYPE);
551
552
template <typename T, template<typename> class Container>
553
void containerInsertTest(QObject*)
554
{
555
    Container<T> container;
556
557
    // insert an item in an empty container
558
    try {
559
        container.insert(container.begin(), 41);
560
    } catch (...) {
561
        QVERIFY(container.isEmpty());
562
        QCOMPARE(T::instanceCount, 0);
563
        return;
564
    }
565
566
    QCOMPARE(container.size(), 1);
567
    QCOMPARE(T::instanceCount, 1);
568
569
    // insert an item before another item
570
    try {
571
        container.insert(container.begin(), 42);
572
    } catch (...) {
573
        QCOMPARE(container.size(), 1);
574
        QCOMPARE(container.first().value(), 41);
575
        QCOMPARE(T::instanceCount, 1);
576
        return;
577
    }
578
579
    QCOMPARE(T::instanceCount, 2);
580
581
    // insert an item in between
582
    try {
583
        container.insert(container.begin() + 1, 43);
584
    } catch (...) {
585
        QCOMPARE(container.size(), 2);
586
        QCOMPARE(container.first().value(), 41);
587
        QCOMPARE((container.begin() + 1)->value(), 42);
588
        QCOMPARE(T::instanceCount, 2);
589
        return;
590
    }
591
592
    QCOMPARE(T::instanceCount, 3);
593
}
594
595
template <typename T, template<typename> class Container>
596
void containerAppendTest(QObject*)
597
{
598
    Container<T> container;
599
600
    // append to an empty container
601
    try {
602
        container.append(42);
603
    } catch (...) {
604
        QCOMPARE(container.size(), 0);
605
        QCOMPARE(T::instanceCount, 0);
606
        return;
607
    }
608
609
    // append to a container with one item
610
    try {
611
        container.append(43);
612
    } catch (...) {
613
        QCOMPARE(container.size(), 1);
614
        QCOMPARE(container.first().value(), 42);
615
        QCOMPARE(T::instanceCount, 1);
616
        return;
617
    }
618
619
    Container<T> container2;
620
621
    try {
622
        container2.append(44);
623
    } catch (...) {
624
        // don't care
625
        return;
626
    }
627
    QCOMPARE(T::instanceCount, 3);
628
629
    // append another container with one item
630
    try {
631
        container += container2;
632
    } catch (...) {
633
        QCOMPARE(container.size(), 2);
634
        QCOMPARE(container.first().value(), 42);
635
        QCOMPARE((container.begin() + 1)->value(), 43);
636
        QCOMPARE(T::instanceCount, 3);
637
        return;
638
    }
639
640
    QCOMPARE(T::instanceCount, 4);
641
}
642
643
template <typename T, template<typename> class Container>
644
void containerEraseTest(QObject*)
645
{
646
    Container<T> container;
647
648
    try {
649
        container.append(42);
650
        container.append(43);
651
        container.append(44);
652
        container.append(45);
653
        container.append(46);
654
    } catch (...) {
655
        // don't care
656
        return;
657
    }
658
659
    // sanity checks
660
    QCOMPARE(container.size(), 5);
661
    QCOMPARE(T::instanceCount, 5);
662
663
    // delete the first one
664
    try {
665
        container.erase(container.begin());
666
    } catch (...) {
667
        QCOMPARE(container.size(), 5);
668
        QCOMPARE(container.first().value(), 42);
669
        QCOMPARE(T::instanceCount, 5);
670
        return;
671
    }
672
673
    QCOMPARE(container.size(), 4);
674
    QCOMPARE(container.first().value(), 43);
675
    QCOMPARE(T::instanceCount, 4);
676
677
    // delete the last one
678
    try {
679
        container.erase(container.end() - 1);
680
    } catch (...) {
681
        QCOMPARE(container.size(), 4);
682
        QCOMPARE(T::instanceCount, 4);
683
        return;
684
    }
685
686
    QCOMPARE(container.size(), 3);
687
    QCOMPARE(container.first().value(), 43);
688
    QCOMPARE((container.begin() + 1)->value(), 44);
689
    QCOMPARE((container.begin() + 2)->value(), 45);
690
    QCOMPARE(T::instanceCount, 3);
691
692
    // delete the middle one
693
    try {
694
        container.erase(container.begin() + 1);
695
    } catch (...) {
696
        QCOMPARE(container.size(), 3);
697
        QCOMPARE(container.first().value(), 43);
698
        QCOMPARE((container.begin() + 1)->value(), 44);
699
        QCOMPARE((container.begin() + 2)->value(), 45);
700
        QCOMPARE(T::instanceCount, 3);
701
        return;
702
    }
703
704
    QCOMPARE(container.size(), 2);
705
    QCOMPARE(container.first().value(), 43);
706
    QCOMPARE((container.begin() + 1)->value(), 45);
707
    QCOMPARE(T::instanceCount, 2);
708
}
709
710
template <template<typename T> class Container>
711
static void containerData()
712
{
713
    QTest::addColumn<TestFunction>("testFunction");
714
715
    QTest::newRow("insert static") << static_cast<TestFunction>(containerInsertTest<Integer, Container>);
716
    QTest::newRow("append static") << static_cast<TestFunction>(containerAppendTest<Integer, Container>);
717
    QTest::newRow("erase static") << static_cast<TestFunction>(containerEraseTest<Integer, Container>);
718
    QTest::newRow("insert moveable") << static_cast<TestFunction>(containerInsertTest<IntegerMoveable, Container>);
719
    QTest::newRow("append moveable") << static_cast<TestFunction>(containerAppendTest<IntegerMoveable, Container>);
720
    QTest::newRow("erase moveable") << static_cast<TestFunction>(containerEraseTest<IntegerMoveable, Container>);
721
}
722
723
void tst_ExceptionSafetyObjects::vector_data()
724
{
725
    containerData<QVector>();
726
}
727
728
void tst_ExceptionSafetyObjects::vector()
729
{
730
    QFETCH(TestFunction, testFunction);
731
732
    if (QLatin1String(QTest::currentDataTag()) == QLatin1String("insert static")
733
        || QLatin1String(QTest::currentDataTag()) == QLatin1String("insert moveable"))
734
        QSKIP("QVector::insert is currently not strongly exception safe", SkipSingle);
735
736
    doOOMTest(testFunction, 0);
737
}
738
739
void tst_ExceptionSafetyObjects::list_data()
740
{
741
    containerData<QList>();
742
}
743
744
void tst_ExceptionSafetyObjects::list()
745
{
746
    QFETCH(TestFunction, testFunction);
747
748
    doOOMTest(testFunction, 0);
749
}
750
751
void tst_ExceptionSafetyObjects::linkedList_data()
752
{
753
    containerData<QLinkedList>();
754
}
755
756
void tst_ExceptionSafetyObjects::linkedList()
757
{
758
    QFETCH(TestFunction, testFunction);
759
760
    doOOMTest(testFunction, 0);
761
}
762
763
QTEST_MAIN(tst_ExceptionSafetyObjects)
764
#include "tst_exceptionsafety_objects.moc"
765
#endif // QT_NO_EXCEPTIONS