1 /* |
|
2 * Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). |
|
3 * All rights reserved. |
|
4 * This component and the accompanying materials are made available |
|
5 * under the terms of "Eclipse Public License v1.0" |
|
6 * which accompanies this distribution, and is available |
|
7 * at the URL "http://www.eclipse.org/legal/epl-v10.html". |
|
8 * |
|
9 * Initial Contributors: |
|
10 * Nokia Corporation - initial contribution. |
|
11 * |
|
12 * Contributors: |
|
13 * |
|
14 * Description: |
|
15 * |
|
16 */ |
|
17 |
|
18 #include <QByteArray> |
|
19 #include "testutilities.h" |
|
20 |
|
21 /*! |
|
22 Replaces global new operator for utilizing binary. Enables OOM |
|
23 simulation and memory leak detection. |
|
24 |
|
25 Note that creation of CBase derived Symbian classes |
|
26 are not tracked, because CBase overloads new operator. |
|
27 */ |
|
28 void* operator new(std::size_t sz) throw(std::bad_alloc) |
|
29 { |
|
30 return MemoryAllocator::alloc(sz); |
|
31 } |
|
32 |
|
33 /*! |
|
34 Replaces global delete operator for utilizing binary. Enables OOM |
|
35 simulation and memory leak detection. |
|
36 */ |
|
37 void operator delete(void* memoryAddr) throw() |
|
38 { |
|
39 return MemoryAllocator::free(memoryAddr); |
|
40 } |
|
41 |
|
42 void* operator new(std::size_t sz, const std::nothrow_t&) throw() |
|
43 { |
|
44 return qMalloc(sz); |
|
45 } |
|
46 |
|
47 void operator delete(void* memoryAddress, const std::nothrow_t&) throw() |
|
48 { |
|
49 if (NULL != memoryAddress) { |
|
50 qFree(memoryAddress); |
|
51 } |
|
52 } |
|
53 |
|
54 bool MemoryAllocator::m_isOomSimulationEnabled = false; |
|
55 int MemoryAllocator::m_numOfAllocsSinceLastFail = 0; |
|
56 int MemoryAllocator::m_allocFailIndex = 1; |
|
57 QList<void*> MemoryAllocator::m_allocList; |
|
58 |
|
59 /*! |
|
60 MemoryAllocator::enableOomSimulation |
|
61 */ |
|
62 void MemoryAllocator::enableOomSimulation() |
|
63 { |
|
64 m_isOomSimulationEnabled = true; |
|
65 m_allocFailIndex = 1; |
|
66 m_numOfAllocsSinceLastFail = 0; |
|
67 } |
|
68 |
|
69 /*! |
|
70 MemoryAllocator::disableOomSimulation |
|
71 */ |
|
72 void MemoryAllocator::disableOomSimulation() |
|
73 { |
|
74 m_isOomSimulationEnabled = false; |
|
75 } |
|
76 |
|
77 /*! |
|
78 MemoryAllocator::isOomSimulationEnabled |
|
79 */ |
|
80 bool MemoryAllocator::isOomSimulationEnabled() |
|
81 { |
|
82 return m_isOomSimulationEnabled; |
|
83 } |
|
84 |
|
85 /*! |
|
86 MemoryAllocator::currentAllocFailIndex |
|
87 */ |
|
88 int MemoryAllocator::currentAllocFailIndex() |
|
89 { |
|
90 return m_allocFailIndex; |
|
91 } |
|
92 |
|
93 /*! |
|
94 MemoryAllocator::alloc |
|
95 */ |
|
96 void* MemoryAllocator::alloc(std::size_t sz) |
|
97 { |
|
98 if (isOomSimulationEnabled()) { |
|
99 m_numOfAllocsSinceLastFail++; |
|
100 if (m_allocFailIndex == m_numOfAllocsSinceLastFail) { |
|
101 m_allocFailIndex++; |
|
102 m_numOfAllocsSinceLastFail = 0; |
|
103 #ifdef QT_NO_EXCEPTIONS |
|
104 return NULL; |
|
105 #else |
|
106 throw std::bad_alloc(); |
|
107 #endif |
|
108 } |
|
109 } |
|
110 |
|
111 void *allocatedMemoryAddr = qMalloc(sz); |
|
112 m_allocList.append(allocatedMemoryAddr); |
|
113 return allocatedMemoryAddr; |
|
114 } |
|
115 |
|
116 /*! |
|
117 MemoryAllocator::free |
|
118 */ |
|
119 void MemoryAllocator::free(void *memoryAddress) |
|
120 { |
|
121 if (memoryAddress) { |
|
122 int index = m_allocList.indexOf(memoryAddress); |
|
123 if (-1 != index) { |
|
124 m_allocList.removeAt(index); |
|
125 } |
|
126 qFree(memoryAddress); |
|
127 } |
|
128 } |
|
129 |
|
130 /*! |
|
131 MemoryAllocator::verifyMemoryAllocations |
|
132 */ |
|
133 void MemoryAllocator::verifyMemoryAllocations() |
|
134 { |
|
135 int numOfUnfreedCells = m_allocList.count(); |
|
136 if (0 != numOfUnfreedCells) { |
|
137 for ( int i = 0; i < numOfUnfreedCells; i++) { |
|
138 qDebug("UNFREED CELL: %x", reinterpret_cast<int>(m_allocList.at(i))); |
|
139 } |
|
140 |
|
141 m_allocList.clear(); |
|
142 throw std::bad_alloc(); |
|
143 } |
|
144 |
|
145 m_allocList.clear(); |
|
146 } |
|
147 |
|
148 |
|
149 /*! |
|
150 OomTestExecuter::runTest |
|
151 */ |
|
152 void OomTestExecuter::runTest( |
|
153 QObject &testObject, const char *testMethod) |
|
154 { |
|
155 qDebug() << "OomTestExecuter::runTest : IN :" << testMethod; |
|
156 |
|
157 MemoryAllocator::enableOomSimulation(); |
|
158 |
|
159 bool exceptionCaught = false; |
|
160 do { |
|
161 exceptionCaught = false; |
|
162 int currentAllocFailIndex = MemoryAllocator::currentAllocFailIndex(); |
|
163 |
|
164 try { |
|
165 try { |
|
166 QMetaObject::invokeMethod( |
|
167 &testObject, "init", Qt::DirectConnection); |
|
168 QMetaObject::invokeMethod( |
|
169 &testObject, testMethod, Qt::DirectConnection); |
|
170 } catch (const std::bad_alloc &ex) { |
|
171 exceptionCaught = true; |
|
172 QMetaObject::invokeMethod( |
|
173 &testObject, "cleanup", Qt::DirectConnection); |
|
174 } |
|
175 // TODO: for some reason bad_alloc exception is corrupted to |
|
176 // unknown exception and nested catch block is needed to be able to |
|
177 // handle situation. One level catch does not work for some reason. |
|
178 } catch (...) { |
|
179 exceptionCaught = true; |
|
180 QMetaObject::invokeMethod( |
|
181 &testObject, "cleanup", Qt::DirectConnection); |
|
182 if (currentAllocFailIndex == MemoryAllocator::currentAllocFailIndex()) { |
|
183 qDebug() << "OomTestExecuter::runTest, ERROR: unexpected exception!"; |
|
184 throw; |
|
185 } |
|
186 } |
|
187 } while(exceptionCaught); |
|
188 |
|
189 QMetaObject::invokeMethod(&testObject, "cleanup", Qt::DirectConnection); |
|
190 MemoryAllocator::disableOomSimulation(); |
|
191 qDebug() << "OomTestExecuter::runTest : OUT :" << testMethod; |
|
192 } |
|
193 |
|
194 /*! |
|
195 OomTestExecuter::runAllTests |
|
196 */ |
|
197 void OomTestExecuter::runAllTests( |
|
198 QObject &testObject, const char *callingTestMethod) |
|
199 { |
|
200 const QMetaObject *metaObject = testObject.metaObject(); |
|
201 |
|
202 int methodCount = metaObject->methodCount(); |
|
203 for (int i = 0; i < methodCount; ++i) { |
|
204 QMetaMethod slotMethodCandidate = metaObject->method(i); |
|
205 if (!isValidSlot(slotMethodCandidate)) { |
|
206 continue; |
|
207 } |
|
208 |
|
209 QByteArray slotMethodName(slotMethodCandidate.signature()); |
|
210 // remove parentheses |
|
211 slotMethodName = slotMethodName.left(slotMethodName.length() - 2); |
|
212 |
|
213 // Prevent from infinite loop and do not execute test method, which |
|
214 // has called runAllTests. |
|
215 if (slotMethodName != callingTestMethod) { |
|
216 runTest(testObject, slotMethodName); |
|
217 } |
|
218 } |
|
219 } |
|
220 |
|
221 /*! |
|
222 OomTestExecuter::isValidSlot |
|
223 */ |
|
224 bool OomTestExecuter::isValidSlot(const QMetaMethod &sl) |
|
225 { |
|
226 if ((sl.access() != QMetaMethod::Private) || !sl.parameterTypes().isEmpty() |
|
227 || qstrlen(sl.typeName()) || (sl.methodType() != QMetaMethod::Slot)) { |
|
228 return false; |
|
229 } |
|
230 |
|
231 const char *sig = sl.signature(); |
|
232 int len = qstrlen(sig); |
|
233 if (len < 2) { |
|
234 return false; |
|
235 } |
|
236 |
|
237 if (sig[len - 2] != '(' || sig[len - 1] != ')') { |
|
238 return false; |
|
239 } |
|
240 |
|
241 if (len > 7 && strcmp(sig + (len - 7), "_data()") == 0) { |
|
242 return false; |
|
243 } |
|
244 |
|
245 if ((strcmp(sig, "initTestCase()") == 0) || (strcmp(sig, "cleanupTestCase()") == 0) |
|
246 || (strcmp(sig, "cleanup()") == 0) || (strcmp(sig, "init()") == 0)) { |
|
247 return false; |
|
248 } |
|
249 |
|
250 return true; |
|
251 } |
|