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