1 /****************************************************************************
2 **
3 ** Copyright (C) 2015 The Qt Company Ltd.
4 ** Contact: http://www.qt.io/licensing/
5 **
6 ** This file is part of the test suite of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see http://www.qt.io/terms-conditions. For further
15 ** information use the contact form at http://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 or version 3 as published by the Free
20 ** Software Foundation and appearing in the file LICENSE.LGPLv21 and
21 ** LICENSE.LGPLv3 included in the packaging of this file. Please review the
22 ** following information to ensure the GNU Lesser General Public License
23 ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
24 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 **
26 ** As a special exception, The Qt Company gives you certain additional
27 ** rights. These rights are described in The Qt Company LGPL Exception
28 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 **
30 ** GNU General Public License Usage
31 ** Alternatively, this file may be used under the terms of the GNU
32 ** General Public License version 3.0 as published by the Free Software
33 ** Foundation and appearing in the file LICENSE.GPL included in the
34 ** packaging of this file. Please review the following information to
35 ** ensure the GNU General Public License version 3.0 requirements will be
36 ** met: http://www.gnu.org/copyleft/gpl.html.
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include <QtTest/QtTest>
43 #include <e32base.h>
44 #include <typeinfo>
45 #include <stdexcept>
46 #include <euserhl.h>
47
48 #ifdef Q_OS_SYMBIAN
49
50 struct QAllocFailAllocator : public RAllocator
51 {
QAllocFailAllocatorQAllocFailAllocator52 QAllocFailAllocator() : allocator(User::Allocator()), allocCount(0), failNext(0)
53 {
54 User::SwitchAllocator(this);
55 }
56
~QAllocFailAllocatorQAllocFailAllocator57 ~QAllocFailAllocator()
58 {
59 User::SwitchAllocator(&allocator);
60 }
61
62 RAllocator& allocator;
63 int allocCount;
64 int failNext;
65
CheckPtrQAllocFailAllocator66 TAny* CheckPtr(TAny* a)
67 {
68 if (a)
69 allocCount++;
70 return a;
71 }
72
CheckFailQAllocFailAllocator73 bool CheckFail()
74 {
75 if (failNext > 0) {
76 failNext--;
77 return true;
78 }
79 return false;
80 }
81
82 // from MAllocator
AllocQAllocFailAllocator83 TAny* Alloc(TInt aSize)
84 {
85 if (CheckFail())
86 return 0;
87 return CheckPtr(allocator.Alloc(aSize));
88 }
89
FreeQAllocFailAllocator90 void Free(TAny* aPtr)
91 {
92 if (aPtr)
93 allocCount--;
94 allocator.Free(aPtr);
95 }
96
ReAllocQAllocFailAllocator97 TAny* ReAlloc(TAny* aPtr, TInt aSize, TInt aMode)
98 {
99 if (CheckFail())
100 return 0;
101 return allocator.ReAlloc(aPtr, aSize, aMode);
102 }
103
AllocLenQAllocFailAllocator104 TInt AllocLen(const TAny* aCell) const
105 {
106 return allocator.AllocLen(aCell);
107 }
108
CompressQAllocFailAllocator109 TInt Compress()
110 {
111 return allocator.Compress();
112 }
113
ResetQAllocFailAllocator114 void Reset()
115 {
116 allocator.Reset();
117 }
118
AllocSizeQAllocFailAllocator119 TInt AllocSize(TInt& aTotalAllocSize) const
120 {
121 return allocator.AllocSize(aTotalAllocSize);
122 }
123
AvailableQAllocFailAllocator124 TInt Available(TInt& aBiggestBlock) const
125 {
126 return allocator.Available(aBiggestBlock);
127 }
128
DebugFunctionQAllocFailAllocator129 TInt DebugFunction(TInt aFunc, TAny* a1, TAny* a2)
130 {
131 return allocator.DebugFunction(aFunc, a1, a2);
132 }
133
Extension_QAllocFailAllocator134 TInt Extension_(TUint aExtensionId, TAny*& a0, TAny* a1)
135 {
136 return ((MAllocator&)allocator).Extension_(aExtensionId, a0, a1);
137 }
138 };
139
140 QAllocFailAllocator testAllocator;
141
142
143 typedef void TLeavingFunc();
144
145 class tst_qmainexceptions : public QObject
146 {
147 Q_OBJECT
148 public:
tst_qmainexceptions()149 tst_qmainexceptions(){};
~tst_qmainexceptions()150 ~tst_qmainexceptions(){};
151
152 void TestSchedulerCatchesError(TLeavingFunc* f, int error);
153 void TestSymbianRoundTrip(int leave, int trap);
154 void TestStdRoundTrip(const std::exception& thrown, const std::exception& caught);
155
156 bool event(QEvent *event);
157
158 public slots:
159 void initTestCase();
160 private slots:
161 void trap();
162 void cleanupstack();
163 void leave();
164 void testTranslateBadAlloc();
165 void testTranslateBigAlloc();
166 void testExceptionFromAO();
167 void testRoundTrip();
168 void testTrap();
169 void testPropagation();
170 void testDtor1();
171 void testDtor2();
172 void testNestedExceptions();
173 void testScopedPointer();
174 void testHybrid();
175 };
176
177 class CDummy : public CBase
178 {
179 public:
CDummy()180 CDummy(){}
~CDummy()181 ~CDummy(){}
182 };
183
initTestCase()184 void tst_qmainexceptions::initTestCase()
185 {
186 }
187
trap()188 void tst_qmainexceptions::trap()
189 {
190 TTrapHandler *th= User::TrapHandler();
191 QVERIFY((int)th);
192 }
193
cleanupstack()194 void tst_qmainexceptions::cleanupstack()
195 {
196 int mark = testAllocator.allocCount;
197 //fails if OOM
198 CDummy* dummy1 = new (ELeave) CDummy;
199 QCOMPARE(mark+1, testAllocator.allocCount);
200 CleanupStack::PushL(dummy1);
201 CleanupStack::PopAndDestroy(dummy1);
202 QCOMPARE(mark, testAllocator.allocCount);
203 }
204
leave()205 void tst_qmainexceptions::leave()
206 {
207 int mark = testAllocator.allocCount;
208 CDummy* dummy1 = 0;
209 TRAPD(err,{
210 CDummy* csDummy = new (ELeave) CDummy;
211 CleanupStack::PushL(csDummy);
212 testAllocator.failNext = 1;
213 dummy1 = new (ELeave) CDummy;
214 //CleanupStack::PopAndDestroy(csDummy); not executed as previous line throws
215 });
216 QCOMPARE(err,KErrNoMemory);
217 QVERIFY(!((int)dummy1));
218 QCOMPARE(mark, testAllocator.allocCount);
219 }
220
221 class CTestActive : public CActive
222 {
223 public:
CTestActive(TLeavingFunc * aFunc,bool qtLoop=false)224 CTestActive(TLeavingFunc* aFunc, bool qtLoop = false) : CActive(EPriorityStandard), iFunc(aFunc), useQtLoop(qtLoop)
225 {
226 CActiveScheduler::Add(this);
227 }
~CTestActive()228 ~CTestActive()
229 {
230 Cancel();
231 }
DoCancel()232 void DoCancel() {}
Test()233 void Test()
234 {
235 // complete this AO in a nested scheduler, to make it synchronous
236 TRequestStatus* s = &iStatus;
237 SetActive();
238 User::RequestComplete(s, KErrNone);
239 StartLoop();
240 }
RunL()241 void RunL()
242 {
243 cleanedUp = false;
244 CleanupStack::PushL(TCleanupItem(CleanUp, &cleanedUp));
245 (*iFunc)();
246 CleanupStack::Pop();
247 StopLoop(); // will only get here if iFunc does not leave
248 }
RunError(TInt aError)249 TInt RunError(TInt aError)
250 {
251 error = aError;
252 StopLoop(); // will only get here if iFunc leaves
253 return KErrNone;
254 }
StartLoop()255 void StartLoop()
256 {
257 if (useQtLoop)
258 qEventLoop.exec();
259 else
260 CActiveScheduler::Start();
261 }
StopLoop()262 void StopLoop()
263 {
264 if (useQtLoop)
265 qEventLoop.exit();
266 else
267 CActiveScheduler::Stop();
268 }
CleanUp(TAny * cleanedUpFlag)269 static void CleanUp(TAny* cleanedUpFlag)
270 {
271 *((bool*)cleanedUpFlag) = true;
272 }
273 public:
274 TLeavingFunc* iFunc;
275 int error;
276 bool useQtLoop;
277 bool cleanedUp;
278 QEventLoop qEventLoop;
279 };
280
TestSchedulerCatchesError(TLeavingFunc * f,int error)281 void tst_qmainexceptions::TestSchedulerCatchesError(TLeavingFunc* f, int error)
282 {
283 for (int i=0; i<2; i++) {
284 CTestActive *act = new(ELeave) CTestActive(f, i==1);
285 act->Test();
286 QCOMPARE(act->error, error);
287 QVERIFY(act->cleanedUp);
288 delete act;
289 }
290 }
291
ThrowBadAlloc()292 void ThrowBadAlloc()
293 {
294 throw std::bad_alloc();
295 }
296
TranslateThrowBadAllocL()297 void TranslateThrowBadAllocL()
298 {
299 QT_TRYCATCH_LEAVING(ThrowBadAlloc());
300 }
301
testTranslateBadAlloc()302 void tst_qmainexceptions::testTranslateBadAlloc()
303 {
304 // bad_alloc should give KErrNoMemory in an AO
305 TestSchedulerCatchesError(&TranslateThrowBadAllocL, KErrNoMemory);
306 }
307
BigAlloc()308 void BigAlloc()
309 {
310 // allocate too much memory - it's expected that 100M ints is too much, but keep doubling if not.
311 int *x = 0;
312 int n = 100000000;
313 do {
314 x = new int[n];
315 delete [] x;
316 n = n * 2;
317 } while (x);
318 }
319
TranslateBigAllocL()320 void TranslateBigAllocL()
321 {
322 QT_TRYCATCH_LEAVING(BigAlloc());
323 }
324
testTranslateBigAlloc()325 void tst_qmainexceptions::testTranslateBigAlloc()
326 {
327 // this test will fail if new does not throw on failure, otherwise should give KErrNoMemory in AO
328 TestSchedulerCatchesError(&TranslateBigAllocL, KErrNoMemory);
329 }
330
testExceptionFromAO()331 void tst_qmainexceptions::testExceptionFromAO()
332 {
333 CTestActive *act = new(ELeave) CTestActive(&ThrowBadAlloc, true);
334 act->Test();
335 QCOMPARE(act->error, KErrNoMemory);
336 QVERIFY(act->cleanedUp);
337 delete act;
338 }
339
TestSymbianRoundTrip(int leave,int trap)340 void tst_qmainexceptions::TestSymbianRoundTrip(int leave, int trap)
341 {
342 // check that leave converted to exception, converted to error gives expected error code
343 int trapped;
344 QT_TRYCATCH_ERROR(
345 trapped,
346 QT_TRAP_THROWING(
347 User::LeaveIfError(leave)));
348 QCOMPARE(trap, trapped);
349 }
350
TestStdRoundTrip(const std::exception & thrown,const std::exception & caught)351 void tst_qmainexceptions::TestStdRoundTrip(const std::exception& thrown, const std::exception& caught)
352 {
353 bool ok = false;
354 try {
355 QT_TRAP_THROWING(qt_symbian_exception2LeaveL(thrown));
356 } catch (const std::exception& ex) {
357 const std::type_info& exType = typeid(ex);
358 const std::type_info& caughtType = typeid(caught);
359 QCOMPARE(exType, caughtType);
360 ok = true;
361 }
362 QCOMPARE(ok, true);
363 }
364
testRoundTrip()365 void tst_qmainexceptions::testRoundTrip()
366 {
367 for (int e=-50; e<0; e++)
368 TestSymbianRoundTrip(e, e);
369 TestSymbianRoundTrip(KErrNone, KErrNone);
370 // positive error codes are not errors
371 TestSymbianRoundTrip(1, KErrNone);
372 TestSymbianRoundTrip(1000000000, KErrNone);
373 TestStdRoundTrip(std::bad_alloc(), std::bad_alloc());
374 TestStdRoundTrip(std::invalid_argument("abc"), std::invalid_argument(""));
375 TestStdRoundTrip(std::underflow_error("abc"), std::underflow_error(""));
376 TestStdRoundTrip(std::overflow_error("abc"), std::overflow_error(""));
377 }
378
testTrap()379 void tst_qmainexceptions::testTrap()
380 {
381 // testing qt_exception2SymbianLeaveL
382 TRAPD(err, qt_symbian_exception2LeaveL(std::bad_alloc()));
383 QCOMPARE(err, KErrNoMemory);
384 }
385
event(QEvent * aEvent)386 bool tst_qmainexceptions::event(QEvent *aEvent)
387 {
388 if (aEvent->type() == QEvent::User+1)
389 throw std::bad_alloc();
390 else if (aEvent->type() == QEvent::User+2) {
391 QEvent event(QEvent::Type(QEvent::User+1));
392 QApplication::sendEvent(this, &event);
393 }
394 return QObject::event(aEvent);
395 }
396
testPropagation()397 void tst_qmainexceptions::testPropagation()
398 {
399 // test exception thrown from event is propagated back to sender
400 QEvent event(QEvent::Type(QEvent::User+1));
401 bool caught = false;
402 try {
403 QApplication::sendEvent(this, &event);
404 } catch (const std::bad_alloc&) {
405 caught = true;
406 }
407 QCOMPARE(caught, true);
408
409 // testing nested events propagate back to top level sender
410 caught = false;
411 QEvent event2(QEvent::Type(QEvent::User+2));
412 try {
413 QApplication::sendEvent(this, &event2);
414 } catch (const std::bad_alloc&) {
415 caught = true;
416 }
417 QCOMPARE(caught, true);
418 }
419
testDtor1()420 void tst_qmainexceptions::testDtor1()
421 {
422 // destructors work on exception
423 int i = 0;
424 struct SAutoInc {
425 SAutoInc(int& aI) : i(aI) { ++i; }
426 ~SAutoInc() { --i; }
427 int &i;
428 } ai(i);
429 QCOMPARE(i, 1);
430 try {
431 SAutoInc ai2(i);
432 QCOMPARE(i, 2);
433 throw std::bad_alloc();
434 QFAIL("should not get here");
435 } catch (const std::bad_alloc&) {
436 QCOMPARE(i, 1);
437 }
438 QCOMPARE(i, 1);
439 }
440
testDtor2()441 void tst_qmainexceptions::testDtor2()
442 {
443 // memory is cleaned up correctly on exception
444 // this crashes with winscw compiler build < 481
445 int mark = testAllocator.allocCount;
446 try {
447 QScopedPointer<QString> ptr(new QString("abc"));
448 QString str("abc");
449 str += "def";
450 throw std::bad_alloc();
451 QFAIL("should not get here");
452 } catch (const std::bad_alloc&) { }
453 QCOMPARE(mark, testAllocator.allocCount);
454 }
455
testNestedExceptions()456 void tst_qmainexceptions::testNestedExceptions()
457 {
458 // throwing exceptions while handling exceptions
459 struct Oops {
460 Oops* next;
461 Oops(int level) : next(level > 0 ? new Oops(level-1) : 0) {}
462 ~Oops() {
463 try { throw std::bad_alloc(); }
464 catch (const std::exception&) {delete next;}
465 }
466 };
467 try {
468 Oops oops(5);
469 throw std::bad_alloc();
470 }
471 catch (const std::exception&) {}
472 }
473
474 class CTestRef : public CBase
475 {
476 public:
CTestRef(int & aX)477 CTestRef(int& aX) : iX(aX) { iX++; }
~CTestRef()478 ~CTestRef() { iX--; }
479 int& iX;
480 };
481
testScopedPointer()482 void tst_qmainexceptions::testScopedPointer()
483 {
484 int x = 0;
485 {
486 QScopedPointer<CTestRef> ptr(q_check_ptr(new CTestRef(x)));
487 QCOMPARE(x, 1);
488 }
489 QCOMPARE(x, 0);
490 try {
491 QScopedPointer<CTestRef> ptr(q_check_ptr(new CTestRef(x)));
492 QCOMPARE(x, 1);
493 throw 1;
494 } catch (int) {
495 QCOMPARE(x, 0);
496 }
497 QCOMPARE(x, 0);
498 }
499
500 int dtorFired[20];
501 int* recDtor;
502
503 class CDtorOrder : public CBase
504 {
505 public:
CDtorOrder(TInt aId)506 CDtorOrder(TInt aId) : iId(aId) {}
~CDtorOrder()507 ~CDtorOrder() { *(recDtor++)=iId; }
508 TInt iId;
509 };
510
511 class QDtorOrder
512 {
513 public:
QDtorOrder(int aId)514 QDtorOrder(int aId) : iId(aId) {}
~QDtorOrder()515 ~QDtorOrder() { *(recDtor++)=iId; }
516 int iId;
517 };
518
519 class RDtorOrder : public RHandleBase
520 {
521 public:
Connect(TInt aId)522 TInt Connect(TInt aId) {iId = aId; SetHandle(aId); return KErrNone; }
Close()523 void Close() { *(recDtor++)=iId; }
524 TInt iId;
525 };
526
527 enum THybridAction {EHybridLeave, EHybridThrow, EHybridPass};
528
HybridFuncLX(THybridAction aAction)529 void HybridFuncLX(THybridAction aAction)
530 {
531 recDtor = dtorFired;
532 QDtorOrder q1(1);
533 {QDtorOrder q2(2);}
534 CDtorOrder* c1 = new(ELeave) CDtorOrder(11);
535 CleanupStack::PushL(c1);
536 {LManagedHandle<RDtorOrder> r1;
537 r1->Connect(21) OR_LEAVE;}
538 CDtorOrder* c2 = new(ELeave) CDtorOrder(12);
539 CleanupStack::PushL(c2);
540 QDtorOrder q3(3);
541 LManagedHandle<RDtorOrder> r2;
542 r2->Connect(22) OR_LEAVE;
543 CDtorOrder* c3 = new(ELeave) CDtorOrder(13);
544 CleanupStack::PushL(c3);
545 CleanupStack::PopAndDestroy(c3);
546 QDtorOrder q4(4);
547 switch (aAction)
548 {
549 case EHybridLeave:
550 User::Leave(KErrNotFound);
551 break;
552 case EHybridThrow:
553 throw std::bad_alloc();
554 break;
555 default:
556 break;
557 }
558 CleanupStack::PopAndDestroy(2);
559 }
560
testHybrid()561 void tst_qmainexceptions::testHybrid()
562 {
563 TRAPD(error,
564 QT_TRYCATCH_LEAVING(
565 HybridFuncLX(EHybridLeave);
566 ) );
567 QCOMPARE(error, KErrNotFound);
568 int expected1[] = {2, 21, 13, 12, 11, 4, 22, 3, 1};
569 QCOMPARE(int(sizeof(expected1)/sizeof(int)), int(recDtor - dtorFired));
570 for (int i=0; i<sizeof(expected1)/sizeof(int); i++)
571 QCOMPARE(expected1[i], dtorFired[i]);
572
573 TRAP(error,
574 QT_TRYCATCH_LEAVING(
575 HybridFuncLX(EHybridThrow);
576 ) );
577 QCOMPARE(error, KErrNoMemory);
578 int expected2[] = {2, 21, 13, 4, 22, 3, 1, 12, 11};
579 QCOMPARE(int(sizeof(expected2)/sizeof(int)), int(recDtor - dtorFired));
580 for (int i=0; i<sizeof(expected2)/sizeof(int); i++)
581 QCOMPARE(expected2[i], dtorFired[i]);
582
583 TRAP(error,
584 QT_TRYCATCH_LEAVING(
585 HybridFuncLX(EHybridPass);
586 ) );
587 QCOMPARE(error, KErrNone);
588 int expected3[] = {2, 21, 13, 12, 11, 4, 22, 3, 1};
589 QCOMPARE(int(sizeof(expected3)/sizeof(int)), int(recDtor - dtorFired));
590 for (int i=0; i<sizeof(expected3)/sizeof(int); i++)
591 QCOMPARE(expected3[i], dtorFired[i]);
592 }
593
594
595 QTEST_MAIN(tst_qmainexceptions)
596 #include "tst_qmainexceptions.moc"
597 #else
598 QTEST_NOOP_MAIN
599 #endif
600