author | Eckhart Koeppen <eckhart.koppen@nokia.com> |
Thu, 22 Apr 2010 16:15:11 +0300 | |
branch | RCL_3 |
changeset 14 | 8c4229025c0b |
parent 4 | 3b1da2848fc7 |
permissions | -rw-r--r-- |
0 | 1 |
/**************************************************************************** |
2 |
** |
|
4
3b1da2848fc7
Revision: 201003
Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
parents:
0
diff
changeset
|
3 |
** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). |
0 | 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 <QtTest/QtTest> |
|
43 |
#include <e32base.h> |
|
44 |
#include <typeinfo> |
|
45 |
#include <stdexcept> |
|
46 |
#include <euserhl.h> |
|
47 |
||
48 |
#ifdef Q_OS_SYMBIAN |
|
49 |
||
50 |
typedef void TLeavingFunc(); |
|
51 |
||
52 |
class tst_qmainexceptions : public QObject |
|
53 |
{ |
|
54 |
Q_OBJECT |
|
55 |
public: |
|
56 |
tst_qmainexceptions(){}; |
|
57 |
~tst_qmainexceptions(){}; |
|
58 |
||
59 |
void TestSchedulerCatchesError(TLeavingFunc* f, int error); |
|
60 |
void TestSymbianRoundTrip(int leave, int trap); |
|
61 |
void TestStdRoundTrip(const std::exception& thrown, const std::exception& caught); |
|
62 |
||
63 |
bool event(QEvent *event); |
|
64 |
||
65 |
public slots: |
|
66 |
void initTestCase(); |
|
67 |
private slots: |
|
68 |
void trap(); |
|
69 |
void cleanupstack(); |
|
70 |
void leave(); |
|
71 |
void testTranslateBadAlloc(); |
|
72 |
void testTranslateBigAlloc(); |
|
73 |
void testRoundTrip(); |
|
74 |
void testTrap(); |
|
75 |
void testPropagation(); |
|
76 |
void testDtor1(); |
|
77 |
void testDtor2(); |
|
78 |
void testNestedExceptions(); |
|
79 |
void testScopedPointer(); |
|
80 |
void testHybrid(); |
|
81 |
}; |
|
82 |
||
83 |
class CDummy : public CBase |
|
84 |
{ |
|
85 |
public: |
|
86 |
CDummy(){} |
|
87 |
~CDummy(){} |
|
88 |
}; |
|
89 |
||
90 |
void tst_qmainexceptions::initTestCase() |
|
91 |
{ |
|
92 |
} |
|
93 |
||
94 |
void tst_qmainexceptions::trap() |
|
95 |
{ |
|
96 |
TTrapHandler *th= User::TrapHandler(); |
|
97 |
QVERIFY((int)th); |
|
98 |
} |
|
99 |
||
100 |
void tst_qmainexceptions::cleanupstack() |
|
101 |
{ |
|
102 |
__UHEAP_MARK; |
|
103 |
//fails if OOM |
|
104 |
CDummy* dummy1 = new (ELeave) CDummy; |
|
105 |
__UHEAP_CHECK(1); |
|
106 |
CleanupStack::PushL(dummy1); |
|
107 |
CleanupStack::PopAndDestroy(dummy1); |
|
108 |
__UHEAP_MARKEND; |
|
109 |
} |
|
110 |
||
111 |
void tst_qmainexceptions::leave() |
|
112 |
{ |
|
113 |
__UHEAP_MARK; |
|
114 |
CDummy* dummy1 = 0; |
|
115 |
TRAPD(err,{ |
|
116 |
CDummy* csDummy = new (ELeave) CDummy; |
|
117 |
CleanupStack::PushL(csDummy); |
|
118 |
__UHEAP_FAILNEXT(1); |
|
119 |
dummy1 = new (ELeave) CDummy; |
|
120 |
//CleanupStack::PopAndDestroy(csDummy); not executed as previous line throws |
|
121 |
}); |
|
122 |
QCOMPARE(err,KErrNoMemory); |
|
123 |
QVERIFY(!((int)dummy1)); |
|
124 |
__UHEAP_MARKEND; |
|
125 |
} |
|
126 |
||
127 |
class CTestActive : public CActive |
|
128 |
{ |
|
129 |
public: |
|
130 |
CTestActive(TLeavingFunc* aFunc) : CActive(EPriorityStandard), iFunc(aFunc) |
|
131 |
{ |
|
132 |
CActiveScheduler::Add(this); |
|
133 |
} |
|
134 |
~CTestActive() |
|
135 |
{ |
|
136 |
Cancel(); |
|
137 |
} |
|
138 |
void DoCancel() {} |
|
139 |
void Test() |
|
140 |
{ |
|
141 |
// complete this AO in a nested scheduler, to make it synchronous |
|
142 |
TRequestStatus* s = &iStatus; |
|
143 |
SetActive(); |
|
144 |
User::RequestComplete(s, KErrNone); |
|
145 |
CActiveScheduler::Start(); |
|
146 |
} |
|
147 |
void RunL() |
|
148 |
{ |
|
149 |
(*iFunc)(); |
|
150 |
CActiveScheduler::Stop(); // will only get here if iFunc does not leave |
|
151 |
} |
|
152 |
TInt RunError(TInt aError) |
|
153 |
{ |
|
154 |
error = aError; |
|
155 |
CActiveScheduler::Stop(); // will only get here if iFunc leaves |
|
156 |
return KErrNone; |
|
157 |
} |
|
158 |
public: |
|
159 |
TLeavingFunc* iFunc; |
|
160 |
int error; |
|
161 |
}; |
|
162 |
||
163 |
void tst_qmainexceptions::TestSchedulerCatchesError(TLeavingFunc* f, int error) |
|
164 |
{ |
|
165 |
CTestActive *act = new(ELeave) CTestActive(f); |
|
166 |
act->Test(); |
|
167 |
QCOMPARE(act->error, error); |
|
168 |
delete act; |
|
169 |
} |
|
170 |
||
171 |
void ThrowBadAlloc() |
|
172 |
{ |
|
173 |
throw std::bad_alloc(); |
|
174 |
} |
|
175 |
||
176 |
void TranslateThrowBadAllocL() |
|
177 |
{ |
|
178 |
QT_TRYCATCH_LEAVING(ThrowBadAlloc()); |
|
179 |
} |
|
180 |
||
181 |
void tst_qmainexceptions::testTranslateBadAlloc() |
|
182 |
{ |
|
183 |
// bad_alloc should give KErrNoMemory in an AO |
|
184 |
TestSchedulerCatchesError(&TranslateThrowBadAllocL, KErrNoMemory); |
|
185 |
} |
|
186 |
||
187 |
void BigAlloc() |
|
188 |
{ |
|
189 |
// allocate too much memory - it's expected that 100M ints is too much, but keep doubling if not. |
|
190 |
int *x = 0; |
|
191 |
int n = 100000000; |
|
192 |
do { |
|
193 |
x = new int[n]; |
|
194 |
delete [] x; |
|
195 |
n = n * 2; |
|
196 |
} while (x); |
|
197 |
} |
|
198 |
||
199 |
void TranslateBigAllocL() |
|
200 |
{ |
|
201 |
QT_TRYCATCH_LEAVING(BigAlloc()); |
|
202 |
} |
|
203 |
||
204 |
void tst_qmainexceptions::testTranslateBigAlloc() |
|
205 |
{ |
|
206 |
// this test will fail if new does not throw on failure, otherwise should give KErrNoMemory in AO |
|
207 |
TestSchedulerCatchesError(&TranslateBigAllocL, KErrNoMemory); |
|
208 |
} |
|
209 |
||
210 |
void tst_qmainexceptions::TestSymbianRoundTrip(int leave, int trap) |
|
211 |
{ |
|
212 |
// check that leave converted to exception, converted to error gives expected error code |
|
213 |
int trapped; |
|
214 |
QT_TRYCATCH_ERROR( |
|
215 |
trapped, |
|
216 |
QT_TRAP_THROWING( |
|
217 |
User::LeaveIfError(leave))); |
|
218 |
QCOMPARE(trap, trapped); |
|
219 |
} |
|
220 |
||
221 |
void tst_qmainexceptions::TestStdRoundTrip(const std::exception& thrown, const std::exception& caught) |
|
222 |
{ |
|
223 |
bool ok = false; |
|
224 |
try { |
|
225 |
QT_TRAP_THROWING(qt_symbian_exception2LeaveL(thrown)); |
|
226 |
} catch (const std::exception& ex) { |
|
227 |
const std::type_info& exType = typeid(ex); |
|
228 |
const std::type_info& caughtType = typeid(caught); |
|
229 |
QCOMPARE(exType, caughtType); |
|
230 |
ok = true; |
|
231 |
} |
|
232 |
QCOMPARE(ok, true); |
|
233 |
} |
|
234 |
||
235 |
void tst_qmainexceptions::testRoundTrip() |
|
236 |
{ |
|
237 |
for (int e=-50; e<0; e++) |
|
238 |
TestSymbianRoundTrip(e, e); |
|
239 |
TestSymbianRoundTrip(KErrNone, KErrNone); |
|
240 |
// positive error codes are not errors |
|
241 |
TestSymbianRoundTrip(1, KErrNone); |
|
242 |
TestSymbianRoundTrip(1000000000, KErrNone); |
|
243 |
TestStdRoundTrip(std::bad_alloc(), std::bad_alloc()); |
|
244 |
TestStdRoundTrip(std::invalid_argument("abc"), std::invalid_argument("")); |
|
245 |
TestStdRoundTrip(std::underflow_error("abc"), std::underflow_error("")); |
|
246 |
TestStdRoundTrip(std::overflow_error("abc"), std::overflow_error("")); |
|
247 |
} |
|
248 |
||
249 |
void tst_qmainexceptions::testTrap() |
|
250 |
{ |
|
251 |
// testing qt_exception2SymbianLeaveL |
|
252 |
TRAPD(err, qt_symbian_exception2LeaveL(std::bad_alloc())); |
|
253 |
QCOMPARE(err, KErrNoMemory); |
|
254 |
} |
|
255 |
||
256 |
bool tst_qmainexceptions::event(QEvent *aEvent) |
|
257 |
{ |
|
258 |
if (aEvent->type() == QEvent::User+1) |
|
259 |
throw std::bad_alloc(); |
|
260 |
else if (aEvent->type() == QEvent::User+2) { |
|
261 |
QEvent event(QEvent::Type(QEvent::User+1)); |
|
262 |
QApplication::sendEvent(this, &event); |
|
263 |
} |
|
264 |
return QObject::event(aEvent); |
|
265 |
} |
|
266 |
||
267 |
void tst_qmainexceptions::testPropagation() |
|
268 |
{ |
|
269 |
// test exception thrown from event is propagated back to sender |
|
270 |
QEvent event(QEvent::Type(QEvent::User+1)); |
|
271 |
bool caught = false; |
|
272 |
try { |
|
273 |
QApplication::sendEvent(this, &event); |
|
274 |
} catch (const std::bad_alloc&) { |
|
275 |
caught = true; |
|
276 |
} |
|
277 |
QCOMPARE(caught, true); |
|
278 |
||
279 |
// testing nested events propagate back to top level sender |
|
280 |
caught = false; |
|
281 |
QEvent event2(QEvent::Type(QEvent::User+2)); |
|
282 |
try { |
|
283 |
QApplication::sendEvent(this, &event2); |
|
284 |
} catch (const std::bad_alloc&) { |
|
285 |
caught = true; |
|
286 |
} |
|
287 |
QCOMPARE(caught, true); |
|
288 |
} |
|
289 |
||
290 |
void tst_qmainexceptions::testDtor1() |
|
291 |
{ |
|
292 |
// destructors work on exception |
|
293 |
int i = 0; |
|
294 |
struct SAutoInc { |
|
295 |
SAutoInc(int& aI) : i(aI) { ++i; } |
|
296 |
~SAutoInc() { --i; } |
|
297 |
int &i; |
|
298 |
} ai(i); |
|
299 |
QCOMPARE(i, 1); |
|
300 |
try { |
|
301 |
SAutoInc ai2(i); |
|
302 |
QCOMPARE(i, 2); |
|
303 |
throw std::bad_alloc(); |
|
304 |
QFAIL("should not get here"); |
|
305 |
} catch (const std::bad_alloc&) { |
|
306 |
QCOMPARE(i, 1); |
|
307 |
} |
|
308 |
QCOMPARE(i, 1); |
|
309 |
} |
|
310 |
||
311 |
void tst_qmainexceptions::testDtor2() |
|
312 |
{ |
|
313 |
// memory is cleaned up correctly on exception |
|
314 |
// this crashes with winscw compiler build < 481 |
|
315 |
__UHEAP_MARK; |
|
316 |
try { |
|
317 |
QString str("abc"); |
|
318 |
str += "def"; |
|
319 |
throw std::bad_alloc(); |
|
320 |
QFAIL("should not get here"); |
|
321 |
} catch (const std::bad_alloc&) { } |
|
322 |
__UHEAP_MARKEND; |
|
323 |
} |
|
324 |
||
325 |
void tst_qmainexceptions::testNestedExceptions() |
|
326 |
{ |
|
327 |
// throwing exceptions while handling exceptions |
|
328 |
struct Oops { |
|
329 |
Oops* next; |
|
330 |
Oops(int level) : next(level > 0 ? new Oops(level-1) : 0) {} |
|
331 |
~Oops() { |
|
332 |
try { throw std::bad_alloc(); } |
|
333 |
catch (const std::exception&) {delete next;} |
|
334 |
} |
|
335 |
}; |
|
336 |
try { |
|
337 |
Oops oops(5); |
|
338 |
throw std::bad_alloc(); |
|
339 |
} |
|
340 |
catch (const std::exception&) {} |
|
341 |
} |
|
342 |
||
343 |
class CTestRef : public CBase |
|
344 |
{ |
|
345 |
public: |
|
346 |
CTestRef(int& aX) : iX(aX) { iX++; } |
|
347 |
~CTestRef() { iX--; } |
|
348 |
int& iX; |
|
349 |
}; |
|
350 |
||
351 |
void tst_qmainexceptions::testScopedPointer() |
|
352 |
{ |
|
353 |
int x = 0; |
|
354 |
{ |
|
355 |
QScopedPointer<CTestRef> ptr(q_check_ptr(new CTestRef(x))); |
|
356 |
QCOMPARE(x, 1); |
|
357 |
} |
|
358 |
QCOMPARE(x, 0); |
|
359 |
try { |
|
360 |
QScopedPointer<CTestRef> ptr(q_check_ptr(new CTestRef(x))); |
|
361 |
QCOMPARE(x, 1); |
|
362 |
throw 1; |
|
363 |
} catch (int) { |
|
364 |
QCOMPARE(x, 0); |
|
365 |
} |
|
366 |
QCOMPARE(x, 0); |
|
367 |
} |
|
368 |
||
369 |
int dtorFired[20]; |
|
370 |
int* recDtor; |
|
371 |
||
372 |
class CDtorOrder : public CBase |
|
373 |
{ |
|
374 |
public: |
|
375 |
CDtorOrder(TInt aId) : iId(aId) {} |
|
376 |
~CDtorOrder() { *(recDtor++)=iId; } |
|
377 |
TInt iId; |
|
378 |
}; |
|
379 |
||
380 |
class QDtorOrder |
|
381 |
{ |
|
382 |
public: |
|
383 |
QDtorOrder(int aId) : iId(aId) {} |
|
384 |
~QDtorOrder() { *(recDtor++)=iId; } |
|
385 |
int iId; |
|
386 |
}; |
|
387 |
||
388 |
class RDtorOrder : public RHandleBase |
|
389 |
{ |
|
390 |
public: |
|
391 |
TInt Connect(TInt aId) {iId = aId; SetHandle(aId); return KErrNone; } |
|
392 |
void Close() { *(recDtor++)=iId; } |
|
393 |
TInt iId; |
|
394 |
}; |
|
395 |
||
396 |
enum THybridAction {EHybridLeave, EHybridThrow, EHybridPass}; |
|
397 |
||
398 |
void HybridFuncLX(THybridAction aAction) |
|
399 |
{ |
|
400 |
recDtor = dtorFired; |
|
401 |
QDtorOrder q1(1); |
|
402 |
{QDtorOrder q2(2);} |
|
403 |
CDtorOrder* c1 = new(ELeave) CDtorOrder(11); |
|
404 |
CleanupStack::PushL(c1); |
|
405 |
{LManagedHandle<RDtorOrder> r1; |
|
406 |
r1->Connect(21) OR_LEAVE;} |
|
407 |
CDtorOrder* c2 = new(ELeave) CDtorOrder(12); |
|
408 |
CleanupStack::PushL(c2); |
|
409 |
QDtorOrder q3(3); |
|
410 |
LManagedHandle<RDtorOrder> r2; |
|
411 |
r2->Connect(22) OR_LEAVE; |
|
412 |
CDtorOrder* c3 = new(ELeave) CDtorOrder(13); |
|
413 |
CleanupStack::PushL(c3); |
|
414 |
CleanupStack::PopAndDestroy(c3); |
|
415 |
QDtorOrder q4(4); |
|
416 |
switch (aAction) |
|
417 |
{ |
|
418 |
case EHybridLeave: |
|
419 |
User::Leave(KErrNotFound); |
|
420 |
break; |
|
421 |
case EHybridThrow: |
|
422 |
throw std::bad_alloc(); |
|
423 |
break; |
|
424 |
default: |
|
425 |
break; |
|
426 |
} |
|
427 |
CleanupStack::PopAndDestroy(2); |
|
428 |
} |
|
429 |
||
430 |
void tst_qmainexceptions::testHybrid() |
|
431 |
{ |
|
432 |
TRAPD(error, |
|
433 |
QT_TRYCATCH_LEAVING( |
|
434 |
HybridFuncLX(EHybridLeave); |
|
435 |
) ); |
|
436 |
QCOMPARE(error, KErrNotFound); |
|
437 |
int expected1[] = {2, 21, 13, 12, 11, 4, 22, 3, 1}; |
|
438 |
QCOMPARE(int(sizeof(expected1)/sizeof(int)), int(recDtor - dtorFired)); |
|
439 |
for (int i=0; i<sizeof(expected1)/sizeof(int); i++) |
|
440 |
QCOMPARE(expected1[i], dtorFired[i]); |
|
441 |
||
442 |
TRAP(error, |
|
443 |
QT_TRYCATCH_LEAVING( |
|
444 |
HybridFuncLX(EHybridThrow); |
|
445 |
) ); |
|
446 |
QCOMPARE(error, KErrNoMemory); |
|
447 |
int expected2[] = {2, 21, 13, 4, 22, 3, 1, 12, 11}; |
|
448 |
QCOMPARE(int(sizeof(expected2)/sizeof(int)), int(recDtor - dtorFired)); |
|
449 |
for (int i=0; i<sizeof(expected2)/sizeof(int); i++) |
|
450 |
QCOMPARE(expected2[i], dtorFired[i]); |
|
451 |
||
452 |
TRAP(error, |
|
453 |
QT_TRYCATCH_LEAVING( |
|
454 |
HybridFuncLX(EHybridPass); |
|
455 |
) ); |
|
456 |
QCOMPARE(error, KErrNone); |
|
457 |
int expected3[] = {2, 21, 13, 12, 11, 4, 22, 3, 1}; |
|
458 |
QCOMPARE(int(sizeof(expected3)/sizeof(int)), int(recDtor - dtorFired)); |
|
459 |
for (int i=0; i<sizeof(expected3)/sizeof(int); i++) |
|
460 |
QCOMPARE(expected3[i], dtorFired[i]); |
|
461 |
} |
|
462 |
||
463 |
||
464 |
QTEST_MAIN(tst_qmainexceptions) |
|
465 |
#include "tst_qmainexceptions.moc" |
|
466 |
#else |
|
467 |
QTEST_NOOP_MAIN |
|
468 |
#endif |