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