/**************************************************************************** ** ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). ** All rights reserved. ** Contact: Nokia Corporation (qt-info@nokia.com) ** ** This file is part of the test suite of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** No Commercial Usage ** This file contains pre-release code and may not be distributed. ** You may use this file in accordance with the terms and conditions ** contained in the Technology Preview License Agreement accompanying ** this package. ** ** 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, Nokia gives you certain additional ** rights. These rights are described in the Nokia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** If you have questions regarding the use of this file, please contact ** Nokia at qt-info@nokia.com. ** ** ** ** ** ** ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include #include #include #include #include #ifdef Q_OS_SYMBIAN typedef void TLeavingFunc(); class tst_qmainexceptions : public QObject { Q_OBJECT public: tst_qmainexceptions(){}; ~tst_qmainexceptions(){}; void TestSchedulerCatchesError(TLeavingFunc* f, int error); void TestSymbianRoundTrip(int leave, int trap); void TestStdRoundTrip(const std::exception& thrown, const std::exception& caught); bool event(QEvent *event); public slots: void initTestCase(); private slots: void trap(); void cleanupstack(); void leave(); void testTranslateBadAlloc(); void testTranslateBigAlloc(); void testRoundTrip(); void testTrap(); void testPropagation(); void testDtor1(); void testDtor2(); void testNestedExceptions(); void testScopedPointer(); void testHybrid(); }; class CDummy : public CBase { public: CDummy(){} ~CDummy(){} }; void tst_qmainexceptions::initTestCase() { } void tst_qmainexceptions::trap() { TTrapHandler *th= User::TrapHandler(); QVERIFY((int)th); } void tst_qmainexceptions::cleanupstack() { __UHEAP_MARK; //fails if OOM CDummy* dummy1 = new (ELeave) CDummy; __UHEAP_CHECK(1); CleanupStack::PushL(dummy1); CleanupStack::PopAndDestroy(dummy1); __UHEAP_MARKEND; } void tst_qmainexceptions::leave() { __UHEAP_MARK; CDummy* dummy1 = 0; TRAPD(err,{ CDummy* csDummy = new (ELeave) CDummy; CleanupStack::PushL(csDummy); __UHEAP_FAILNEXT(1); dummy1 = new (ELeave) CDummy; //CleanupStack::PopAndDestroy(csDummy); not executed as previous line throws }); QCOMPARE(err,KErrNoMemory); QVERIFY(!((int)dummy1)); __UHEAP_MARKEND; } class CTestActive : public CActive { public: CTestActive(TLeavingFunc* aFunc) : CActive(EPriorityStandard), iFunc(aFunc) { CActiveScheduler::Add(this); } ~CTestActive() { Cancel(); } void DoCancel() {} void Test() { // complete this AO in a nested scheduler, to make it synchronous TRequestStatus* s = &iStatus; SetActive(); User::RequestComplete(s, KErrNone); CActiveScheduler::Start(); } void RunL() { (*iFunc)(); CActiveScheduler::Stop(); // will only get here if iFunc does not leave } TInt RunError(TInt aError) { error = aError; CActiveScheduler::Stop(); // will only get here if iFunc leaves return KErrNone; } public: TLeavingFunc* iFunc; int error; }; void tst_qmainexceptions::TestSchedulerCatchesError(TLeavingFunc* f, int error) { CTestActive *act = new(ELeave) CTestActive(f); act->Test(); QCOMPARE(act->error, error); delete act; } void ThrowBadAlloc() { throw std::bad_alloc(); } void TranslateThrowBadAllocL() { QT_TRYCATCH_LEAVING(ThrowBadAlloc()); } void tst_qmainexceptions::testTranslateBadAlloc() { // bad_alloc should give KErrNoMemory in an AO TestSchedulerCatchesError(&TranslateThrowBadAllocL, KErrNoMemory); } void BigAlloc() { // allocate too much memory - it's expected that 100M ints is too much, but keep doubling if not. int *x = 0; int n = 100000000; do { x = new int[n]; delete [] x; n = n * 2; } while (x); } void TranslateBigAllocL() { QT_TRYCATCH_LEAVING(BigAlloc()); } void tst_qmainexceptions::testTranslateBigAlloc() { // this test will fail if new does not throw on failure, otherwise should give KErrNoMemory in AO TestSchedulerCatchesError(&TranslateBigAllocL, KErrNoMemory); } void tst_qmainexceptions::TestSymbianRoundTrip(int leave, int trap) { // check that leave converted to exception, converted to error gives expected error code int trapped; QT_TRYCATCH_ERROR( trapped, QT_TRAP_THROWING( User::LeaveIfError(leave))); QCOMPARE(trap, trapped); } void tst_qmainexceptions::TestStdRoundTrip(const std::exception& thrown, const std::exception& caught) { bool ok = false; try { QT_TRAP_THROWING(qt_symbian_exception2LeaveL(thrown)); } catch (const std::exception& ex) { const std::type_info& exType = typeid(ex); const std::type_info& caughtType = typeid(caught); QCOMPARE(exType, caughtType); ok = true; } QCOMPARE(ok, true); } void tst_qmainexceptions::testRoundTrip() { for (int e=-50; e<0; e++) TestSymbianRoundTrip(e, e); TestSymbianRoundTrip(KErrNone, KErrNone); // positive error codes are not errors TestSymbianRoundTrip(1, KErrNone); TestSymbianRoundTrip(1000000000, KErrNone); TestStdRoundTrip(std::bad_alloc(), std::bad_alloc()); TestStdRoundTrip(std::invalid_argument("abc"), std::invalid_argument("")); TestStdRoundTrip(std::underflow_error("abc"), std::underflow_error("")); TestStdRoundTrip(std::overflow_error("abc"), std::overflow_error("")); } void tst_qmainexceptions::testTrap() { // testing qt_exception2SymbianLeaveL TRAPD(err, qt_symbian_exception2LeaveL(std::bad_alloc())); QCOMPARE(err, KErrNoMemory); } bool tst_qmainexceptions::event(QEvent *aEvent) { if (aEvent->type() == QEvent::User+1) throw std::bad_alloc(); else if (aEvent->type() == QEvent::User+2) { QEvent event(QEvent::Type(QEvent::User+1)); QApplication::sendEvent(this, &event); } return QObject::event(aEvent); } void tst_qmainexceptions::testPropagation() { // test exception thrown from event is propagated back to sender QEvent event(QEvent::Type(QEvent::User+1)); bool caught = false; try { QApplication::sendEvent(this, &event); } catch (const std::bad_alloc&) { caught = true; } QCOMPARE(caught, true); // testing nested events propagate back to top level sender caught = false; QEvent event2(QEvent::Type(QEvent::User+2)); try { QApplication::sendEvent(this, &event2); } catch (const std::bad_alloc&) { caught = true; } QCOMPARE(caught, true); } void tst_qmainexceptions::testDtor1() { // destructors work on exception int i = 0; struct SAutoInc { SAutoInc(int& aI) : i(aI) { ++i; } ~SAutoInc() { --i; } int &i; } ai(i); QCOMPARE(i, 1); try { SAutoInc ai2(i); QCOMPARE(i, 2); throw std::bad_alloc(); QFAIL("should not get here"); } catch (const std::bad_alloc&) { QCOMPARE(i, 1); } QCOMPARE(i, 1); } void tst_qmainexceptions::testDtor2() { // memory is cleaned up correctly on exception // this crashes with winscw compiler build < 481 __UHEAP_MARK; try { QString str("abc"); str += "def"; throw std::bad_alloc(); QFAIL("should not get here"); } catch (const std::bad_alloc&) { } __UHEAP_MARKEND; } void tst_qmainexceptions::testNestedExceptions() { // throwing exceptions while handling exceptions struct Oops { Oops* next; Oops(int level) : next(level > 0 ? new Oops(level-1) : 0) {} ~Oops() { try { throw std::bad_alloc(); } catch (const std::exception&) {delete next;} } }; try { Oops oops(5); throw std::bad_alloc(); } catch (const std::exception&) {} } class CTestRef : public CBase { public: CTestRef(int& aX) : iX(aX) { iX++; } ~CTestRef() { iX--; } int& iX; }; void tst_qmainexceptions::testScopedPointer() { int x = 0; { QScopedPointer ptr(q_check_ptr(new CTestRef(x))); QCOMPARE(x, 1); } QCOMPARE(x, 0); try { QScopedPointer ptr(q_check_ptr(new CTestRef(x))); QCOMPARE(x, 1); throw 1; } catch (int) { QCOMPARE(x, 0); } QCOMPARE(x, 0); } int dtorFired[20]; int* recDtor; class CDtorOrder : public CBase { public: CDtorOrder(TInt aId) : iId(aId) {} ~CDtorOrder() { *(recDtor++)=iId; } TInt iId; }; class QDtorOrder { public: QDtorOrder(int aId) : iId(aId) {} ~QDtorOrder() { *(recDtor++)=iId; } int iId; }; class RDtorOrder : public RHandleBase { public: TInt Connect(TInt aId) {iId = aId; SetHandle(aId); return KErrNone; } void Close() { *(recDtor++)=iId; } TInt iId; }; enum THybridAction {EHybridLeave, EHybridThrow, EHybridPass}; void HybridFuncLX(THybridAction aAction) { recDtor = dtorFired; QDtorOrder q1(1); {QDtorOrder q2(2);} CDtorOrder* c1 = new(ELeave) CDtorOrder(11); CleanupStack::PushL(c1); {LManagedHandle r1; r1->Connect(21) OR_LEAVE;} CDtorOrder* c2 = new(ELeave) CDtorOrder(12); CleanupStack::PushL(c2); QDtorOrder q3(3); LManagedHandle r2; r2->Connect(22) OR_LEAVE; CDtorOrder* c3 = new(ELeave) CDtorOrder(13); CleanupStack::PushL(c3); CleanupStack::PopAndDestroy(c3); QDtorOrder q4(4); switch (aAction) { case EHybridLeave: User::Leave(KErrNotFound); break; case EHybridThrow: throw std::bad_alloc(); break; default: break; } CleanupStack::PopAndDestroy(2); } void tst_qmainexceptions::testHybrid() { TRAPD(error, QT_TRYCATCH_LEAVING( HybridFuncLX(EHybridLeave); ) ); QCOMPARE(error, KErrNotFound); int expected1[] = {2, 21, 13, 12, 11, 4, 22, 3, 1}; QCOMPARE(int(sizeof(expected1)/sizeof(int)), int(recDtor - dtorFired)); for (int i=0; i