/****************************************************************************
**
** Copyright (C) 2009 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 <QtTest/QtTest>
#include <e32base.h>
#include <typeinfo>
#include <stdexcept>
#include <euserhl.h>
#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<CTestRef> ptr(q_check_ptr(new CTestRef(x)));
QCOMPARE(x, 1);
}
QCOMPARE(x, 0);
try {
QScopedPointer<CTestRef> 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<RDtorOrder> r1;
r1->Connect(21) OR_LEAVE;}
CDtorOrder* c2 = new(ELeave) CDtorOrder(12);
CleanupStack::PushL(c2);
QDtorOrder q3(3);
LManagedHandle<RDtorOrder> 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<sizeof(expected1)/sizeof(int); i++)
QCOMPARE(expected1[i], dtorFired[i]);
TRAP(error,
QT_TRYCATCH_LEAVING(
HybridFuncLX(EHybridThrow);
) );
QCOMPARE(error, KErrNoMemory);
int expected2[] = {2, 21, 13, 4, 22, 3, 1, 12, 11};
QCOMPARE(int(sizeof(expected2)/sizeof(int)), int(recDtor - dtorFired));
for (int i=0; i<sizeof(expected2)/sizeof(int); i++)
QCOMPARE(expected2[i], dtorFired[i]);
TRAP(error,
QT_TRYCATCH_LEAVING(
HybridFuncLX(EHybridPass);
) );
QCOMPARE(error, KErrNone);
int expected3[] = {2, 21, 13, 12, 11, 4, 22, 3, 1};
QCOMPARE(int(sizeof(expected3)/sizeof(int)), int(recDtor - dtorFired));
for (int i=0; i<sizeof(expected3)/sizeof(int); i++)
QCOMPARE(expected3[i], dtorFired[i]);
}
QTEST_MAIN(tst_qmainexceptions)
#include "tst_qmainexceptions.moc"
#else
QTEST_NOOP_MAIN
#endif