|
1 /* |
|
2 * Copyright (c) 2005-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 * Implementation of the following classes |
|
16 * CTestServer |
|
17 * n x CTestSession Maps to RTestServ session) |
|
18 * n x CStepControl or CPersistentStepControl (Maps to RTestSession) |
|
19 * CTestStep (1 to 1 mapping with parent) |
|
20 * CTestServer |
|
21 * Derives CServer |
|
22 * Test servers derive from this and implement the CreateTestStep() pure virtual |
|
23 * CTestSession |
|
24 * Derives CSharableSession |
|
25 * Implements the ServiceL() pure virtual and creates CStepControl object(s) |
|
26 * when a test step is opened. |
|
27 * Implements the MSessionNotify interface for callbacks from CStepControl when |
|
28 * a test step completes. |
|
29 * CStepControl |
|
30 * Derives CActive |
|
31 * Runs a test step instance in its own thread and heap |
|
32 * CTestStep |
|
33 * Derives CBase |
|
34 * Test Servers derive test steps from this |
|
35 * |
|
36 */ |
|
37 |
|
38 |
|
39 |
|
40 /** |
|
41 @file TestClient.cpp |
|
42 */ |
|
43 |
|
44 #include "TestExecuteServerBase.h" |
|
45 #include "TestServer2.h" |
|
46 #include "TestExecuteServerUtils.h" |
|
47 #include "TestExecuteClient.h" |
|
48 #include <e32math.h> |
|
49 #include <wrapperutilsplugin.h> |
|
50 #include <test/tefutils.h> |
|
51 |
|
52 const TUint KDefaultHeapSize = 0x100000; |
|
53 |
|
54 //> @internalComponent |
|
55 // see the impletation below. |
|
56 void SytemWideErrToTefErr(TInt &aErr); |
|
57 /** |
|
58 * Constructor |
|
59 */ |
|
60 EXPORT_C CTestServer::CTestServer() : CServer2(EPriorityStandard) |
|
61 , iSeed(0) |
|
62 , iSessionCount(0) |
|
63 { |
|
64 // Random seed for unique thread id's |
|
65 iSeed = (TInt)this; |
|
66 // Default is not to allow Server Logging |
|
67 iLoggerStarted = EFalse; |
|
68 } |
|
69 |
|
70 /** |
|
71 * Destructor |
|
72 */ |
|
73 EXPORT_C CTestServer::~CTestServer() |
|
74 { |
|
75 if (iLoggerStarted) |
|
76 { |
|
77 // Shut down the Servers' logger instance. |
|
78 Logger().Close(); |
|
79 } |
|
80 } |
|
81 |
|
82 /** |
|
83 * @param aName - Reference to the Server name |
|
84 * StartL + initiate server logging |
|
85 * Servers can StartL themselves or call this to gain server logging. |
|
86 */ |
|
87 EXPORT_C void CTestServer::ConstructL(const TDesC& aName) |
|
88 { |
|
89 StartL(aName); |
|
90 StartLoggerL(); |
|
91 iServerName = aName; |
|
92 } |
|
93 |
|
94 /** |
|
95 * Will extract the script logfile name from the temporary file 'LogFileName.txt' |
|
96 * (assuming no ScheduleTest compatible logging) and then opens a logging session |
|
97 * to that file. If ScheduleTest logging is in effect then we will open ScriptEngine.htm |
|
98 * instead as the file to log to. |
|
99 */ |
|
100 void CTestServer::StartLoggerL() |
|
101 { |
|
102 TDriveName defaultSysDrive(KTEFLegacySysDrive); |
|
103 RFs fileServer; |
|
104 TVersionName version(fileServer.Version().Name()); |
|
105 |
|
106 RLibrary pluginLibrary; |
|
107 CWrapperUtilsPlugin* plugin = TEFUtils::WrapperPluginNew(pluginLibrary); |
|
108 |
|
109 if (plugin!=NULL) |
|
110 { |
|
111 TDriveUnit driveUnit(plugin->GetSystemDrive()); |
|
112 defaultSysDrive.Copy(driveUnit.Name()); |
|
113 delete plugin; |
|
114 pluginLibrary.Close(); |
|
115 } |
|
116 |
|
117 CTestExecuteIniData* parseTestExecuteIni = NULL; |
|
118 TBuf<KMaxTestExecuteNameLength> resultFilePath; |
|
119 TBuf<KMaxTestExecuteNameLength> xmlFilePath; |
|
120 TInt logMode; |
|
121 TInt logLevel; |
|
122 |
|
123 TRAPD(err,parseTestExecuteIni = CTestExecuteIniData::NewL(defaultSysDrive)); |
|
124 if (err == KErrNone) |
|
125 { |
|
126 CleanupStack::PushL(parseTestExecuteIni); |
|
127 parseTestExecuteIni->ExtractValuesFromIni(); |
|
128 parseTestExecuteIni->GetKeyValueFromIni(KTEFHtmlKey, resultFilePath); |
|
129 parseTestExecuteIni->GetKeyValueFromIni(KTEFXmlKey, xmlFilePath); |
|
130 parseTestExecuteIni->GetKeyValueFromIni(KTEFLogMode, logMode); |
|
131 parseTestExecuteIni->GetKeyValueFromIni(KTEFLogSeverityKey, logLevel); |
|
132 } |
|
133 else |
|
134 { |
|
135 TBuf<KMaxTestExecuteNameLength> testExecuteLogPath(KTestExecuteLogPath); |
|
136 testExecuteLogPath.Replace(0, 2, defaultSysDrive); |
|
137 resultFilePath.Copy(testExecuteLogPath); |
|
138 xmlFilePath.Copy(testExecuteLogPath); |
|
139 logMode = TLoggerOptions(ELogHTMLOnly); |
|
140 logLevel = RFileFlogger::TLogSeverity(ESevrAll); |
|
141 } |
|
142 Logger().SetLoggerOptions(logMode); |
|
143 |
|
144 // Initialise a handle to the file logger |
|
145 User::LeaveIfError(Logger().Connect()); |
|
146 RFs fS; |
|
147 User::LeaveIfError(fS.Connect()); |
|
148 CleanupClosePushL(fS); |
|
149 RFile file; |
|
150 TBuf<KMaxTestExecuteNameLength> xmlLogFile(xmlFilePath); |
|
151 TBuf<KMaxTestExecuteNameLength> logFile; |
|
152 TBuf<KMaxTestExecuteNameLength> logFileNameFile(resultFilePath); |
|
153 logFileNameFile.Append(KTestExecuteScheduleTestLogCompatibilityNameFile); |
|
154 if(file.Open(fS,logFileNameFile,EFileRead | EFileShareAny) != KErrNone) |
|
155 { |
|
156 // If LogFileName.txt is not present then we are using ScheduleTest |
|
157 // compliant logging, any logging issued by the server will therefore |
|
158 // go to ScriptEngine.htm |
|
159 _LIT(KScriptEngine,"ScriptEngine"); |
|
160 logFile.Copy(KScriptEngine); |
|
161 } |
|
162 else |
|
163 { |
|
164 CleanupClosePushL(file); |
|
165 TBuf8<KMaxTestExecuteNameLength> logFile8; |
|
166 TInt fileSize; |
|
167 User::LeaveIfError(file.Size(fileSize)); |
|
168 User::LeaveIfError(file.Read(logFile8,fileSize)); |
|
169 logFile.Copy(logFile8); |
|
170 CleanupStack::Pop(&file); |
|
171 file.Close(); |
|
172 } |
|
173 |
|
174 xmlLogFile.Append(logFile); |
|
175 _LIT(KXmlExtension,".xml"); |
|
176 xmlLogFile.Append(KXmlExtension); |
|
177 _LIT(KHtmExtension,".htm"); |
|
178 logFile.Append(KHtmExtension); |
|
179 TBuf<KMaxTestExecuteLogFilePath> logFilePath(resultFilePath); |
|
180 logFilePath.Append(logFile); |
|
181 CleanupStack::Pop(&fS); |
|
182 fS.Close(); |
|
183 |
|
184 if (logMode == 0 || logMode == 2) |
|
185 { |
|
186 User::LeaveIfError(Logger().HtmlLogger().CreateLog(logFilePath,RTestExecuteLogServ::ELogModeAppend)); |
|
187 Logger().HtmlLogger().SetLogLevel(TLogSeverity(logLevel)); |
|
188 } |
|
189 |
|
190 if (logMode == 1 || logMode == 2) |
|
191 { |
|
192 User::LeaveIfError(Logger().XmlLogger().CreateLog(xmlLogFile,RFileFlogger::ELogModeAppend)); |
|
193 Logger().XmlLogger().SetLogLevel(RFileFlogger::TLogSeverity(logLevel)); |
|
194 } |
|
195 |
|
196 User::LeaveIfError( Logger().ShareAuto() ); |
|
197 iLoggerStarted = ETrue; |
|
198 if (parseTestExecuteIni != NULL) |
|
199 { |
|
200 CleanupStack::PopAndDestroy(parseTestExecuteIni); |
|
201 } |
|
202 } |
|
203 |
|
204 /** |
|
205 * Last one out switch off the lights |
|
206 * Stop the active sheduler and hence the server, if this is the last session |
|
207 */ |
|
208 void CTestServer::SessionClosed() |
|
209 { |
|
210 iSessionCount--; |
|
211 if (iSessionCount == 0) |
|
212 CActiveScheduler::Stop(); |
|
213 } |
|
214 |
|
215 /** |
|
216 * @param RMessage - RMessage for the session open |
|
217 * Secure version |
|
218 */ |
|
219 EXPORT_C CSession2* CTestServer::NewSessionL(const TVersion& /*aVersion*/,const RMessage2& /*aMessage*/) const |
|
220 { |
|
221 CTestSession* session = new (ELeave) CTestSession(); |
|
222 CONST_CAST(CTestServer*,this)->iSessionCount++; |
|
223 return session; |
|
224 } |
|
225 |
|
226 /** |
|
227 * Constructor |
|
228 */ |
|
229 EXPORT_C CTestSession::CTestSession() |
|
230 : iPersistentStepControl(NULL) |
|
231 , iPersistentBlockControl(NULL) |
|
232 , iBlockArray(NULL) |
|
233 { |
|
234 } |
|
235 |
|
236 /** |
|
237 * Destructor |
|
238 */ |
|
239 EXPORT_C CTestSession::~CTestSession() |
|
240 { |
|
241 if( iBlockArray ) |
|
242 { |
|
243 delete iBlockArray; |
|
244 iBlockArray = NULL; |
|
245 } |
|
246 |
|
247 CTestServer* p=(CTestServer*) Server(); |
|
248 |
|
249 //delete the persistent step |
|
250 if(iPersistentStepControl) |
|
251 delete iPersistentStepControl; |
|
252 //delete the persistent block |
|
253 if(iPersistentBlockControl) |
|
254 delete iPersistentBlockControl; |
|
255 // Shuts Down the server if this is the last open session |
|
256 p->SessionClosed(); |
|
257 } |
|
258 |
|
259 /** |
|
260 * @param aMessage - Function and data for the session |
|
261 * Session was created by pure virtual CTestServer::NewSessionL() |
|
262 * Message Functions defined in TestExecuteClient.h |
|
263 * |
|
264 * EOpenTestStep - Creates a new subsession |
|
265 * ERunTestStep - Executes the test step asynchronously |
|
266 * EAbortTestStep - Kill()'s the executing test step |
|
267 * ECloseTestStep - Free's the resource |
|
268 * |
|
269 * Secure and non-secure variants |
|
270 * There are two modes of operation: |
|
271 * Test step is opened with the shared data boolean set to FALSE: |
|
272 * Create a new CStepControl instance and hence a new thread in its own heap |
|
273 * Consecutive or Concurrent operation |
|
274 * Test step is opened with the shared data boolean set to TRUE: |
|
275 * Create a CPersistentStepControl and keep reusing it, and its thread |
|
276 * Consecutive operation only |
|
277 */ |
|
278 EXPORT_C void CTestSession::ServiceL(const RMessage2& aMessage) |
|
279 { |
|
280 switch(aMessage.Function()) |
|
281 { |
|
282 case EOpenTestStep : |
|
283 { |
|
284 // Open the test step |
|
285 // Buffer size policed on the client side |
|
286 TBuf<KMaxTestStepNameLength> stepName; |
|
287 // Read the step name from the descriptor |
|
288 TBool sharedData; |
|
289 aMessage.ReadL(0,stepName); |
|
290 // Find out what mode we're working in |
|
291 sharedData = aMessage.Int1(); |
|
292 // Both types derive from base class and implement pure virtuals |
|
293 CControlBase* stepControl = NULL; |
|
294 if(sharedData) |
|
295 { |
|
296 // Shared data mode |
|
297 // Create the instance if it doesn't exist |
|
298 if(!iPersistentStepControl) |
|
299 iPersistentStepControl = new (ELeave)CPersistentStepControl(*(CTestServer*)Server()); |
|
300 stepControl = iPersistentStepControl; |
|
301 iPersistentStepControl->StepName() = stepName; |
|
302 } |
|
303 else |
|
304 { |
|
305 // Default operation. Create a new instance |
|
306 stepControl = new (ELeave)CStepControl(*(CTestServer*)Server(),stepName); |
|
307 } |
|
308 // We pass back the address of the CStepControl class which is passed to |
|
309 // us in all calls on the subsession in Message 3 |
|
310 TPtrC8 stepRef(REINTERPRET_CAST(TUint8*,&stepControl),sizeof(TInt)); |
|
311 aMessage.Write(3,stepRef); |
|
312 aMessage.Complete(KErrNone); |
|
313 } |
|
314 break; |
|
315 case EOpenTestBlock : |
|
316 { |
|
317 // Open the test block |
|
318 // Buffer size policed on the client side |
|
319 TBuf<KMaxTestExecuteNameLength> stepName; |
|
320 |
|
321 // Find out what mode we're working in |
|
322 TBool sharedData = aMessage.Int1(); |
|
323 // Both types derive from base class and implement pure virtuals |
|
324 CBlockControlBase* blockControl = NULL; |
|
325 if(sharedData) |
|
326 { |
|
327 // Shared data mode |
|
328 // Create the instance if it doesn't exist |
|
329 if(!iPersistentBlockControl) |
|
330 iPersistentBlockControl = new (ELeave)CPersistentBlockControl(*(CTestServer*)Server()); |
|
331 blockControl = iPersistentBlockControl; |
|
332 } |
|
333 else |
|
334 { |
|
335 // Default operation. Create a new instance |
|
336 blockControl = new (ELeave)CBlockControl(*(CTestServer*)Server()); |
|
337 } |
|
338 // We pass back the address of the CStepControl class which is passed to |
|
339 // us in all calls on the subsession in Message 3 |
|
340 TPtrC8 blockRef(REINTERPRET_CAST(TUint8*,&blockControl),sizeof(TInt)); |
|
341 aMessage.Write(3,blockRef); |
|
342 aMessage.Complete(KErrNone); |
|
343 } |
|
344 break; |
|
345 case ERunTestStep : |
|
346 { |
|
347 // Execute the test step |
|
348 // Buffer size policed on client side |
|
349 // Message 0 contains the test step arguments |
|
350 // Message 1 contains a descriptor for the panic string, if the test step panics |
|
351 TBuf<KMaxTestExecuteCommandLength> stepArgs; |
|
352 aMessage.ReadL(0,stepArgs); |
|
353 |
|
354 // Get the address of our CStepControl object |
|
355 CStepControlBase* stepControl = REINTERPRET_CAST(CStepControlBase*,aMessage.Int3()); |
|
356 // Kick off the test step |
|
357 // Message completed when the test step completes |
|
358 // StartL() is mode dependent pure virtual |
|
359 TRAPD(err,stepControl->StartL(aMessage, stepArgs)); |
|
360 if(err) |
|
361 // Complete now if we can't start the test step |
|
362 // Client has possibly called run before waiting for last completion |
|
363 aMessage.Complete(err); |
|
364 } |
|
365 break; |
|
366 case ERunTestBlock : |
|
367 { |
|
368 // Execute the test step |
|
369 // Buffer size policed on client side |
|
370 // Message 0 contains the test step arguments |
|
371 // Message 1 contains a descriptor for the panic string, if the test step panics |
|
372 TBuf<KMaxTestExecuteCommandLength> blockArgs; |
|
373 aMessage.ReadL(0,blockArgs); |
|
374 |
|
375 // Get the test block of commands |
|
376 HBufC8* itemArray = HBufC8::NewLC( aMessage.GetDesMaxLengthL(2) ); |
|
377 TPtr8 itemArrayPtr( itemArray->Des() ); |
|
378 aMessage.ReadL( 2, itemArrayPtr ); |
|
379 |
|
380 // Get the address of our CStepControl object |
|
381 CBlockControlBase* blockControl = REINTERPRET_CAST(CBlockControlBase*,aMessage.Int3()); |
|
382 // Kick off the test step |
|
383 // Message completed when the test step completes |
|
384 // StartL() is mode dependent pure virtual |
|
385 TRAPD(err,blockControl->StartL(aMessage, blockArgs, itemArrayPtr )); |
|
386 if(err) |
|
387 // Complete now if we can't start the test step |
|
388 // Client has possibly called run before waiting for last completion |
|
389 aMessage.Complete(err); |
|
390 |
|
391 CleanupStack::PopAndDestroy( itemArray ); |
|
392 } |
|
393 break; |
|
394 case EAbortTestStep : |
|
395 { |
|
396 CControlBase* control = REINTERPRET_CAST(CControlBase*,aMessage.Int3()); |
|
397 // Stop is mode dependent pure virtual |
|
398 control->Stop(); |
|
399 // Complete synchronously |
|
400 aMessage.Complete(KErrNone); |
|
401 break; |
|
402 } |
|
403 case ECloseTestStep : |
|
404 { |
|
405 CControlBase* control = REINTERPRET_CAST(CControlBase*,aMessage.Int3()); |
|
406 // Only delete if we are in non-shared data mode |
|
407 if( control != iPersistentStepControl && |
|
408 control != iPersistentBlockControl ) |
|
409 { |
|
410 delete control; |
|
411 } |
|
412 aMessage.Complete(KErrNone); |
|
413 } |
|
414 break; |
|
415 default: |
|
416 break; |
|
417 } |
|
418 } |
|
419 |
|
420 /** |
|
421 * @param aServer - Reference to the CTestServer base class |
|
422 * @param aStepName - The test step name |
|
423 * The Non-Shared data step control implementation |
|
424 */ |
|
425 CStepControl::CStepControl(CTestServer& aServer, const TDesC& aStepName) : |
|
426 CStepControlBase(aServer) |
|
427 { |
|
428 StepName() = aStepName; |
|
429 } |
|
430 |
|
431 /** |
|
432 * Thread completion |
|
433 */ |
|
434 void CStepControl::RunL() |
|
435 { |
|
436 // Error value if set in the test step will be saved in the Message() |
|
437 if (Error() != KNull) |
|
438 { |
|
439 TBuf<KMaxTestExecuteNameLength> errorParam(KErrorEquals); |
|
440 errorParam.Append(Error()); |
|
441 Message().Write(1,errorParam); |
|
442 } |
|
443 |
|
444 // If the thread panicked, pick up the panic string and return it to the client |
|
445 // Overwrites the error value previously saved in Message() |
|
446 if(WorkerThread().ExitType() == EExitPanic) |
|
447 { |
|
448 TBuf<KMaxTestExecuteNameLength> panicParam(KPanicEquals); |
|
449 panicParam.Append(WorkerThread().ExitCategory()); |
|
450 Message().Write(1,panicParam); |
|
451 } |
|
452 |
|
453 |
|
454 |
|
455 if (WorkerThread().ExitType() == EExitPanic) |
|
456 { |
|
457 TInt err = WorkerThread().ExitReason(); |
|
458 SytemWideErrToTefErr(err); |
|
459 Message().Complete(err); |
|
460 } |
|
461 else |
|
462 { |
|
463 if (iStatus.Int() == KErrAbort && TimedOut()) |
|
464 { |
|
465 if (Server().LoggerStarted()) |
|
466 { |
|
467 Server().ERR_PRINTF1(_L("TEST IS ABOUT TO ABORT DUE TO TEF TIMEOUT")); |
|
468 } |
|
469 } |
|
470 // iStatus.Int() is the same as the thread ExitReason |
|
471 Message().Complete(iStatus.Int()); |
|
472 } |
|
473 |
|
474 // Close thread handle |
|
475 WorkerThread().Close(); |
|
476 } |
|
477 |
|
478 /** |
|
479 * Destructor |
|
480 */ |
|
481 CStepControl::~CStepControl() |
|
482 { |
|
483 } |
|
484 |
|
485 /** |
|
486 * Step Execution module which is wrapped within UHEAP macros to trap memory leaks during execution |
|
487 * @param aStepControl - Is a generic type of class T passed as template parameter. Either CStepControl/CWorkerControl |
|
488 * for normal execution mode and persistant thread mode for concurrent execution of steps respectively |
|
489 * @param aStep - Object derived from CTestStep class for execution of test steps both in normal mode and persistent mode |
|
490 */ |
|
491 template<class T> |
|
492 void ThreadStepExecutionL(T* aStepControl, CTestStep* aStep) |
|
493 { |
|
494 TInt loop = 0; |
|
495 TBool simulateOOM = EFalse; //ShouldRunOOMTest(); |
|
496 |
|
497 FOREVER |
|
498 { |
|
499 TInt errRun = KErrNone; |
|
500 |
|
501 // Call the CTestStep virtuals |
|
502 aStep->doTestStepPreambleL(); |
|
503 |
|
504 // count cells so we can know how many we leaked |
|
505 TInt cellsStart = User::CountAllocCells(); |
|
506 |
|
507 if (simulateOOM) |
|
508 { |
|
509 __UHEAP_MARK; |
|
510 |
|
511 // set allocator to fail on the loop'th alloc |
|
512 aStep->SetHeapFailNext(loop); |
|
513 } |
|
514 |
|
515 aStepControl->TimedOut() = ETrue; |
|
516 TRAP(errRun, aStep->doTestStepL()); |
|
517 if (errRun != KErrNone && !simulateOOM) |
|
518 { |
|
519 if (errRun == KErrAbort) |
|
520 { |
|
521 aStepControl->TimedOut() = EFalse; |
|
522 } |
|
523 aStep->doTestStepPostambleL(); |
|
524 User::Leave(errRun); |
|
525 } |
|
526 |
|
527 TBool finishedCorrectly = EFalse; |
|
528 // cancel the alloc failures |
|
529 if (simulateOOM) |
|
530 { |
|
531 if ((errRun == KErrNone) && (loop >= 1)) |
|
532 { |
|
533 // claims to have finished correctly, and we're not failing every alloc |
|
534 finishedCorrectly = aStep->CheckForHeapFailNext(); |
|
535 } |
|
536 aStep->ResetHeapFailNext(); |
|
537 } |
|
538 |
|
539 aStep->doTestStepPostambleL(); |
|
540 |
|
541 TInt cellsEnd = User::CountAllocCells(); |
|
542 |
|
543 if (cellsStart < cellsEnd && simulateOOM) |
|
544 { |
|
545 // leaked. |
|
546 TInt leakedCells = cellsEnd - cellsStart; |
|
547 if (aStepControl->Server().LoggerStarted()) |
|
548 { |
|
549 aStepControl->Server().ERR_PRINTF3(_L("On loop number %d we leaked %d cells. About to cause panic."),loop,leakedCells); |
|
550 } |
|
551 aStep->SetTestStepResult(EFail); |
|
552 } |
|
553 |
|
554 if (simulateOOM) |
|
555 { |
|
556 // panic on leak (alloc nnnnnnnn) |
|
557 __UHEAP_MARKEND; |
|
558 } |
|
559 |
|
560 // check to see if we finished all OOM testing successfully |
|
561 if ((errRun == KErrNone) && (simulateOOM) && (finishedCorrectly)) |
|
562 { |
|
563 // test completed successfully, or the User::Leave(KErrNoMemory) was trapped by something else. |
|
564 // Need a cunning solution here. Hmm. Testing to see if the next alloc call fails won't work: |
|
565 // eg, if a test has 3 allocs, heap currently set to fail every 2nd, this would be number 4, |
|
566 // and if 2 was masked then we would think we are done. |
|
567 // |
|
568 // Fix PDEF115450, remove the line aStep->SetTestStepResult(EPass); and modify the information |
|
569 // to "Out of memory test completed after %d iterations." |
|
570 if (aStepControl->Server().LoggerStarted()) |
|
571 { |
|
572 aStepControl->Server().INFO_PRINTF2(_L("Out of memory test completed after %d iterations."),loop); |
|
573 } |
|
574 break; |
|
575 } |
|
576 |
|
577 // check to see if we should run OOM testing. |
|
578 if (++loop == 1) |
|
579 { |
|
580 // first go. |
|
581 if (!aStep->ShouldRunOOMTest()) |
|
582 break; |
|
583 else |
|
584 { |
|
585 if (aStepControl->Server().LoggerStarted()) |
|
586 { |
|
587 aStepControl->Server().INFO_PRINTF1(_L("Test passed. About to run Out of Memory testing.")); |
|
588 } |
|
589 simulateOOM = ETrue; |
|
590 aStep->IniAccessLog() = EFalse; |
|
591 } |
|
592 } |
|
593 } |
|
594 } |
|
595 |
|
596 /** |
|
597 * @param aStepControl - Pointer to the step control object which kicked us off |
|
598 * The thread code. Just drops through with no reuse. |
|
599 */ |
|
600 void ThreadFuncL(CStepControl* aStepControl) |
|
601 { |
|
602 // Call the server pure virtual to get a step instance |
|
603 CTestStep* step = CONST_CAST(CTestServer&,aStepControl->Server()).CreateTestStep(aStepControl->StepName()); |
|
604 if(!step) |
|
605 User::Leave(KErrNotFound); |
|
606 CleanupStack::PushL(step); |
|
607 // Set up the step base class members |
|
608 TBool sharedData = EFalse; |
|
609 step->InitialiseL(aStepControl->Args(), aStepControl->Server().Name(), sharedData); |
|
610 |
|
611 ThreadStepExecutionL(aStepControl, step); |
|
612 |
|
613 // Return Error value set in test step to log result for comparison |
|
614 if (step->TestStepError() != 0) |
|
615 aStepControl->Error().Num(step->TestStepError()); |
|
616 // EPass is 0 |
|
617 // All the rest should be TRAP'd |
|
618 if(step->TestStepResult()) |
|
619 User::Leave(step->TestStepResult()); |
|
620 CleanupStack::PopAndDestroy(step); |
|
621 } |
|
622 |
|
623 /** |
|
624 * @param aParam - Pointer to a CStepControl object |
|
625 * The thread entry method |
|
626 */ |
|
627 TInt ThreadFunc(TAny* aParam) |
|
628 { |
|
629 // Create the thread's cleanup stack |
|
630 CTrapCleanup* cleanup = CTrapCleanup::New(); |
|
631 if(!cleanup) |
|
632 return KErrNoMemory; |
|
633 // Trap it and return the error code to the OS |
|
634 TRAPD(err, ThreadFuncL(REINTERPRET_CAST(CStepControl*,aParam))); |
|
635 SytemWideErrToTefErr(err); |
|
636 delete cleanup; |
|
637 cleanup = NULL; |
|
638 return err; |
|
639 } |
|
640 |
|
641 /** |
|
642 * @param aMessage - Keep a reference for async completion |
|
643 * @param aStepArgs - The RUN_TEST_STEP arguments |
|
644 * Secure and non-secure variants |
|
645 * Kick off the test step in its own thread |
|
646 * Pure virtual implementation |
|
647 */ |
|
648 void CStepControl::StartL(const RMessage2& aMessage, const TDesC& aStepArgs) |
|
649 { |
|
650 if(IsActive()) |
|
651 User::Leave(KErrInUse); |
|
652 Message() = aMessage; |
|
653 Args().Copy(aStepArgs); |
|
654 TBuf<8> heapSizeBuf(KNull); |
|
655 TUint heapSize(0); |
|
656 aMessage.ReadL(1,heapSizeBuf); |
|
657 aMessage.Write(1,KNull); |
|
658 TLex heapSizeLex; |
|
659 |
|
660 if (heapSizeBuf.Length() >=3) |
|
661 { |
|
662 if ( heapSizeBuf.Mid(0,2).CompareF(_L("0x")) == 0 ) |
|
663 { |
|
664 heapSizeLex.Assign(heapSizeBuf.Mid(2)); |
|
665 } |
|
666 else |
|
667 { |
|
668 heapSizeLex.Assign(heapSizeBuf); |
|
669 } |
|
670 heapSizeLex.Val(heapSize,EHex); |
|
671 } |
|
672 |
|
673 TBuf<50> threadName; |
|
674 // Unique thread name guaranteed if we use the this pointer plus a random number |
|
675 // whose seed was initialised to the address of the CTestServer object |
|
676 _LIT(KWorker,"Worker%d %d"); |
|
677 threadName.Format(KWorker,(TInt)this,Math::Rand(CONST_CAST(CTestServer&,Server()).RandSeed())); |
|
678 // Create with own heap so system cleans up if we kill it |
|
679 const TUint KMaxHeapSize = 0x100000; |
|
680 const TUint KMinSize = KMinHeapSize; |
|
681 if (heapSize < KMinSize) |
|
682 heapSize = KMaxHeapSize; ///< Allow a 1Mb max heap |
|
683 |
|
684 User::LeaveIfError(WorkerThread().Create(threadName, ThreadFunc, KDefaultStackSize + 0x1000,KMinHeapSize, heapSize,this, EOwnerProcess)); |
|
685 // Prime ready for completion |
|
686 SetActive(); |
|
687 // Use the appropriate variant call to get the thread exit |
|
688 WorkerThread().Logon(iStatus); |
|
689 WorkerThread().Resume(); |
|
690 } |
|
691 |
|
692 /** |
|
693 * Kill the thread if it's still running |
|
694 * The async completion will be picked up as normal with a KErrAbort status |
|
695 * Pure virtual implementation |
|
696 */ |
|
697 void CStepControl::Stop() |
|
698 { |
|
699 if(IsActive()) |
|
700 WorkerThread().Kill(KErrAbort); |
|
701 } |
|
702 |
|
703 /** |
|
704 * @param aStepControl - Pointer to the step control object. |
|
705 * The test step thread. |
|
706 * We reuse this thread so the test steps can store persistent data in the |
|
707 * CTestServer derived class |
|
708 * The thread synchronises with its creator via the CWorkerControl class |
|
709 * Implementation of the shared data mode control object |
|
710 */ |
|
711 void PersistentThreadFuncL(CWorkerControl* aControl) |
|
712 { |
|
713 // Thread entry is sync'd with a semaphore |
|
714 // Caller will Wait on this |
|
715 // Also set our main sync treq to pending. |
|
716 // It's completed to let us go in and execute the test step code. |
|
717 aControl->WorkerStatus() = KRequestPending; |
|
718 aControl->Semaphore().Signal(); |
|
719 // Go into the main test step execution loop |
|
720 for(;;) |
|
721 { |
|
722 User::WaitForRequest(aControl->WorkerStatus()); |
|
723 // Check |
|
724 if(aControl->WorkerStatus().Int() == KErrAbort) |
|
725 User::Leave(KErrAbort); |
|
726 CTestStep* step = CONST_CAST(CTestServer&,aControl->Server()).CreateTestStep(aControl->StepName()); |
|
727 if(!step) |
|
728 User::Leave(KErrNotFound); |
|
729 CleanupStack::PushL(step); |
|
730 // Set up the step base class members |
|
731 TBool sharedData = ETrue; |
|
732 step->InitialiseL(aControl->Args(), aControl->Server().Name(), sharedData); |
|
733 |
|
734 ThreadStepExecutionL(aControl, step); |
|
735 |
|
736 // Pick up the final result |
|
737 // Set it in the controlling class |
|
738 aControl->Result() = step->TestStepResult(); |
|
739 TBuf<KMaxTestExecuteNameLength> lError; |
|
740 if (step->TestStepError() != 0) |
|
741 { |
|
742 lError.Num(step->TestStepError()); |
|
743 if (lError != KNull) |
|
744 { |
|
745 lError.Insert(0,KErrorEquals); |
|
746 aControl->PersistentError().Copy(lError); |
|
747 } |
|
748 } |
|
749 CleanupStack::PopAndDestroy(step); |
|
750 // Set our status for the wait at the top of the loop |
|
751 aControl->WorkerStatus() = KRequestPending; |
|
752 // Signal the status that our creator will be waiting on |
|
753 // Creator's thread handle in the control class |
|
754 TRequestStatus* status = &aControl->Status(); |
|
755 aControl->ControllerThread().RequestComplete(status,KErrNone); |
|
756 } |
|
757 } |
|
758 |
|
759 /** |
|
760 * @param aParam - Pointer to a CTestStep control object |
|
761 * The thread entry method |
|
762 */ |
|
763 TInt PersistentThreadFunc(TAny* aParam) |
|
764 { |
|
765 // Create the thread's cleanup stack |
|
766 CTrapCleanup* cleanup = CTrapCleanup::New(); |
|
767 |
|
768 if(!cleanup) |
|
769 return KErrNoMemory; |
|
770 // Trap it and return the error code to the OS |
|
771 //defect 116046 |
|
772 CWorkerControl* workControl = REINTERPRET_CAST(CWorkerControl*,aParam); |
|
773 workControl->SetCleanupPtr(cleanup); |
|
774 TRAPD(err, PersistentThreadFuncL(workControl)); |
|
775 //END defect 116046 |
|
776 SytemWideErrToTefErr(err); |
|
777 delete cleanup; |
|
778 cleanup = NULL; |
|
779 return err; |
|
780 } |
|
781 |
|
782 /** |
|
783 * @param aServer - Reference to the CTestServer derived class |
|
784 * Constructor |
|
785 */ |
|
786 CPersistentStepControl::CPersistentStepControl(CTestServer& aServer) : |
|
787 CStepControlBase(aServer), |
|
788 iInitialised(EFalse) |
|
789 { |
|
790 } |
|
791 |
|
792 /** |
|
793 * @param aMessage - Client's message for completion |
|
794 * @param aStepArgs - Arguments to the RUN_xxx_STEP_COMMAND's |
|
795 * Implementation of base class pure virtual. |
|
796 * Necessarily complex because of thread reuse. Instantiates 2 classes: |
|
797 * WorkerMonitor class and WorkerControl class |
|
798 * WorkerMonitor picks up thread exit and WorkerControl picks up test step |
|
799 * return value. |
|
800 */ |
|
801 void CPersistentStepControl::StartL(const RMessage2& aMessage,const TDesC& aStepArgs) |
|
802 { |
|
803 if(IsActive()) |
|
804 User::Leave(KErrInUse); |
|
805 Message() = aMessage; |
|
806 Args().Copy(aStepArgs); |
|
807 // Check to see if we're reusing the worker thread and classes |
|
808 if(!iInitialised) |
|
809 { |
|
810 // Need to construct the monitor and controller classes |
|
811 // They are both constructed with a reference to our iStatus |
|
812 // Either of them can complete us. We check their Active flags in our RunL() |
|
813 iWorkerControl = new (ELeave) CWorkerControl(Server(),iStatus); |
|
814 // The worker thread needs our thread handle to RequestComplete us |
|
815 User::LeaveIfError(iWorkerControl->ControllerThread().Duplicate(RThread())); |
|
816 // Worker thread entry is sync'd with a semaphore. |
|
817 User::LeaveIfError(iWorkerControl->Semaphore().CreateLocal(0)); |
|
818 TBuf<50> threadName; |
|
819 // Unique thread name guaranteed if we use the this pointer plus a random number |
|
820 // whose seed was initialised to the address of the CTestServer object |
|
821 // Create in our heap. |
|
822 _LIT(KWorker,"Worker%d %d"); |
|
823 threadName.Format(KWorker,(TInt)this,Math::Rand(CONST_CAST(CTestServer&,Server()).RandSeed())); |
|
824 User::LeaveIfError(WorkerThread().Create(threadName,PersistentThreadFunc, KDefaultStackSize + 0x1000,NULL,iWorkerControl, EOwnerProcess)); |
|
825 iWorkerMonitor = new (ELeave) CWorkerMonitor(iStatus); |
|
826 } |
|
827 // Worker thread needs the step arguments and the step name |
|
828 iWorkerControl->Args().Set(Args()); |
|
829 iWorkerControl->StepName().Set(StepName()); |
|
830 // Set this object ready for completion by either the monitor or controller objects |
|
831 Prime(); |
|
832 // Set the child monitor and control objects ready for completion |
|
833 iWorkerMonitor->SetActive(); |
|
834 iWorkerControl->Prime(); |
|
835 // Use the monitor object to pick up thread exit |
|
836 // This should only happen for panic, leave and abort following the Stop() call |
|
837 WorkerThread().Rendezvous(iWorkerMonitor->Status()); |
|
838 if(!iInitialised) |
|
839 { |
|
840 // Start the thread and sync up via the semaphore |
|
841 WorkerThread().Resume(); |
|
842 iWorkerControl->Semaphore().Wait(); |
|
843 iWorkerControl->Semaphore().Close(); |
|
844 iInitialised = ETrue; |
|
845 } |
|
846 // Worker thread will be at the top of its loop waiting to execute |
|
847 // the test step virtuals. |
|
848 // Issue the request then it will drop through |
|
849 TRequestStatus* status = &iWorkerControl->WorkerStatus(); |
|
850 WorkerThread().RequestComplete(status,KErrNone); |
|
851 } |
|
852 |
|
853 /** |
|
854 * Destructor |
|
855 */ |
|
856 CPersistentStepControl::~CPersistentStepControl() |
|
857 { |
|
858 // Only need to clean up in the initialised state |
|
859 if(!iInitialised) |
|
860 return; |
|
861 |
|
862 // Check both objects |
|
863 // Neither of them should be active, but just in case |
|
864 if(iWorkerMonitor->IsActive()) |
|
865 { |
|
866 // Cancelling means we don't get stray events |
|
867 WorkerThread().RendezvousCancel(iWorkerMonitor->Status()); |
|
868 // Need to cancel the objeect itself |
|
869 iWorkerMonitor->Cancel(); |
|
870 } |
|
871 if(iWorkerControl->IsActive()) |
|
872 { |
|
873 // Complete the request then cancel |
|
874 TRequestStatus* status = &iWorkerControl->Status(); |
|
875 User::RequestComplete(status,KErrNone); |
|
876 iWorkerControl->Cancel(); |
|
877 } |
|
878 |
|
879 // The worker thread will currently be blocked on its TRequestStatus at the top |
|
880 // of its loop. |
|
881 // Signal the status with KErrAbort and the thread will check this value and leave. |
|
882 // If we Kill the thread then the cleanup stack for the thread is orphaned. |
|
883 // PersistentThreadFuncL() TRAP's the leave. |
|
884 // We logon and catch the thread exit. |
|
885 TRequestStatus status = KRequestPending; |
|
886 WorkerThread().Rendezvous(status); |
|
887 TRequestStatus* workerStatus = &iWorkerControl->WorkerStatus(); |
|
888 WorkerThread().RequestComplete(workerStatus,KErrAbort); |
|
889 User::WaitForRequest(status); |
|
890 // Close both handles |
|
891 WorkerThread().Close(); |
|
892 iWorkerControl->ControllerThread().Close(); |
|
893 |
|
894 delete iWorkerControl; |
|
895 delete iWorkerMonitor; |
|
896 } |
|
897 |
|
898 /** |
|
899 * Necessarily complex because of the two sources of completion |
|
900 * We can tell which one completed us by checking their iActive members |
|
901 */ |
|
902 void CPersistentStepControl::RunL() |
|
903 { |
|
904 if (iWorkerControl->PersistentError() != KNull) |
|
905 { |
|
906 TBuf<KMaxTestExecuteNameLength> errorParam; |
|
907 errorParam.Copy(iWorkerControl->PersistentError()); // Error Value returned as Panic Result |
|
908 Message().Write(1,errorParam); |
|
909 iWorkerControl->PersistentError().Copy(KNull); |
|
910 } |
|
911 TInt ret = KErrNone; |
|
912 // Check which of the child objects completed us |
|
913 if(!iWorkerMonitor->IsActive()) |
|
914 { |
|
915 // Unexpected exit from the worker thread |
|
916 iInitialised = EFalse;// this also make ~CPersistentStepControl not to delete twice. |
|
917 // Pick up the exit reason and panic code if it exists |
|
918 if(WorkerThread().ExitType() == EExitPanic) |
|
919 { |
|
920 TBuf<KMaxTestExecuteNameLength> panicParam(KPanicEquals); |
|
921 panicParam.Append(WorkerThread().ExitCategory()); // Panic Value returned as Result |
|
922 |
|
923 //START defect 116046, Cleanup memories. |
|
924 //iWorkerControl->Cleanup(); |
|
925 //END defect 116046 |
|
926 |
|
927 Message().Write(1,panicParam); |
|
928 } |
|
929 |
|
930 ret = WorkerThread().ExitReason(); |
|
931 |
|
932 if (WorkerThread().ExitType() == EExitPanic) |
|
933 { |
|
934 SytemWideErrToTefErr(ret); |
|
935 } |
|
936 if (ret == KErrAbort && iWorkerControl->TimedOut()) |
|
937 { |
|
938 if (Server().LoggerStarted()) |
|
939 { |
|
940 Server().ERR_PRINTF1(_L("TEST IS ABOUT TO ABORT DUE TO TEF TIMEOUT")); |
|
941 } |
|
942 } |
|
943 |
|
944 // We need to complete and cancel the other request so we don't have stray events |
|
945 TRequestStatus* status = &iWorkerControl->Status(); |
|
946 User::RequestComplete(status,KErrNone); |
|
947 iWorkerControl->Cancel(); |
|
948 // Free the resource in the worker control object |
|
949 iWorkerControl->ControllerThread().Close(); |
|
950 WorkerThread().Close(); |
|
951 delete iWorkerControl; |
|
952 iWorkerControl = NULL; |
|
953 delete iWorkerMonitor; |
|
954 iWorkerMonitor = NULL; |
|
955 // Next time in to StartL() we create them from cleana |
|
956 } |
|
957 else if(!iWorkerControl->IsActive()) |
|
958 { |
|
959 // Normal test step completion |
|
960 // We can reuse the thread next time into StartL() |
|
961 // The thread will be blocking on iWorkerStatus |
|
962 // We need to cancel the other object |
|
963 WorkerThread().RendezvousCancel(iWorkerMonitor->Status()); |
|
964 iWorkerMonitor->Cancel(); |
|
965 // Retrieve the test result |
|
966 ret = iWorkerControl->Result(); |
|
967 SytemWideErrToTefErr(ret); |
|
968 } |
|
969 else |
|
970 // Unexpected |
|
971 { |
|
972 ret = iStatus.Int(); |
|
973 } |
|
974 // Complete back to the client |
|
975 Message().Complete(ret); |
|
976 } |
|
977 |
|
978 /** |
|
979 * Abort due to timeout |
|
980 * The worker monitor object will pick up the thread exit |
|
981 */ |
|
982 void CPersistentStepControl::Stop() |
|
983 { |
|
984 if(iWorkerMonitor->IsActive()) |
|
985 { |
|
986 WorkerThread().Kill(KErrAbort); |
|
987 } |
|
988 } |
|
989 |
|
990 |
|
991 CBlockControlBase::~CBlockControlBase() |
|
992 { |
|
993 if( iBlockArray ) |
|
994 { |
|
995 delete iBlockArray; |
|
996 iBlockArray = NULL; |
|
997 } |
|
998 } |
|
999 |
|
1000 TTEFItemArray* CBlockControlBase::BlockArray() const |
|
1001 { |
|
1002 return iBlockArray; |
|
1003 } |
|
1004 |
|
1005 void CBlockControlBase::CreateBlockArrayL( const TDesC8& aBlockArrayPckg ) |
|
1006 { |
|
1007 if( iBlockArray ) |
|
1008 { |
|
1009 delete iBlockArray; |
|
1010 iBlockArray = NULL; |
|
1011 } |
|
1012 |
|
1013 TTEFItemPkgBuf itemPckgBuf; |
|
1014 TInt count = aBlockArrayPckg.Size()/itemPckgBuf.Size(); |
|
1015 iBlockArray = new (ELeave) TTEFItemArray( count ); |
|
1016 TInt pos = 0; |
|
1017 for( TInt i=0; i<count; i++ ) |
|
1018 { |
|
1019 itemPckgBuf.Copy(aBlockArrayPckg.Mid(pos, itemPckgBuf.Size())); |
|
1020 pos += itemPckgBuf.Size(); |
|
1021 iBlockArray->AppendL( itemPckgBuf() ); |
|
1022 } |
|
1023 } |
|
1024 |
|
1025 HBufC8* CBlockControlBase::CreateBlockArrayPckgLC() |
|
1026 { |
|
1027 TInt count = iBlockArray->Count(); |
|
1028 TTEFItemPkgBuf itemPckgBuf; |
|
1029 HBufC8* blockArrayPckg = HBufC8::NewLC( count * itemPckgBuf.Size() ); |
|
1030 TPtr8 ptr( blockArrayPckg->Des() ); |
|
1031 for( TInt i=0; i<count; i++ ) |
|
1032 { |
|
1033 itemPckgBuf = iBlockArray->At(i); |
|
1034 ptr.Append( itemPckgBuf ); |
|
1035 } |
|
1036 |
|
1037 return blockArrayPckg; |
|
1038 } |
|
1039 |
|
1040 CBlockControl::CBlockControl(CTestServer& aServer) |
|
1041 : CBlockControlBase(aServer) |
|
1042 { |
|
1043 } |
|
1044 |
|
1045 CBlockControl::~CBlockControl() |
|
1046 { |
|
1047 } |
|
1048 |
|
1049 void CBlockControl::RunL() |
|
1050 { |
|
1051 // Write back the test block |
|
1052 HBufC8* blockArrayPckg = CreateBlockArrayPckgLC(); |
|
1053 TPtr8 blockArrayPtr(blockArrayPckg->Des()); |
|
1054 Message().WriteL( 2, blockArrayPtr ); |
|
1055 CleanupStack::PopAndDestroy(blockArrayPckg); |
|
1056 |
|
1057 // If the thread panicked, pick up the panic string and return it to the client |
|
1058 // Overwrites the error value previously saved in Message() |
|
1059 if(WorkerThread().ExitType() == EExitPanic) |
|
1060 { |
|
1061 TBuf<KMaxTestExecuteNameLength> panicParam(KPanicEquals); |
|
1062 TPtrC panicCat = WorkerThread().ExitCategory(); // Panic Value returned as Result |
|
1063 if( panicCat.Length() == 0 ) |
|
1064 { |
|
1065 panicParam.Append(_L("NULL")); |
|
1066 } |
|
1067 panicParam.Append( panicCat ); |
|
1068 Message().Write(1,panicParam); |
|
1069 } |
|
1070 |
|
1071 if (WorkerThread().ExitType() == EExitPanic) |
|
1072 { |
|
1073 TInt err = WorkerThread().ExitReason(); |
|
1074 SytemWideErrToTefErr(err); |
|
1075 Message().Complete(err); |
|
1076 } |
|
1077 else |
|
1078 { |
|
1079 if (iStatus.Int() == KErrAbort && TimedOut()) |
|
1080 { |
|
1081 if (Server().LoggerStarted()) |
|
1082 { |
|
1083 Server().ERR_PRINTF1(_L("TEST IS ABOUT TO ABORT DUE TO TEF TIMEOUT")); |
|
1084 } |
|
1085 } |
|
1086 // iStatus.Int() is the same as the thread ExitReason |
|
1087 Message().Complete(iStatus.Int()); |
|
1088 } |
|
1089 |
|
1090 // Close thread handle |
|
1091 WorkerThread().Close(); |
|
1092 } |
|
1093 |
|
1094 |
|
1095 void ThreadBlockFuncL(CBlockControl* aBlockControl) |
|
1096 { |
|
1097 // Call the server pure virtual to get a step instance |
|
1098 CTestBlockController* block = REINTERPRET_CAST(CTestServer2&,aBlockControl->Server()).CreateTestBlock(); |
|
1099 if(!block) |
|
1100 { |
|
1101 User::Leave(KErrNotFound); |
|
1102 } |
|
1103 CleanupStack::PushL(block); |
|
1104 |
|
1105 // Set up the block base class members |
|
1106 TBool sharedData = EFalse; |
|
1107 block->InitialiseL(aBlockControl->Args(), aBlockControl->Server().Name(), sharedData); |
|
1108 block->SetBlockArray(aBlockControl->BlockArray()); |
|
1109 block->SetSharedData( REINTERPRET_CAST(CTestServer2*, &aBlockControl->Server() )); |
|
1110 |
|
1111 ThreadStepExecutionL(aBlockControl, block); |
|
1112 |
|
1113 // All the rest should be TRAP'd |
|
1114 if( block->TestStepResult() ) |
|
1115 { |
|
1116 User::Leave( block->TestStepResult() ); |
|
1117 } |
|
1118 |
|
1119 CleanupStack::PopAndDestroy(block); |
|
1120 } |
|
1121 |
|
1122 TInt ThreadBlockFunc(TAny* aParam) |
|
1123 { |
|
1124 // Create the thread's cleanup stack |
|
1125 CTrapCleanup* cleanup = CTrapCleanup::New(); |
|
1126 if(!cleanup) |
|
1127 return KErrNoMemory; |
|
1128 // Trap it and return the error code to the OS |
|
1129 TRAPD(err, ThreadBlockFuncL(REINTERPRET_CAST(CBlockControl*,aParam))); |
|
1130 SytemWideErrToTefErr(err); |
|
1131 delete cleanup; |
|
1132 cleanup = NULL; |
|
1133 return err; |
|
1134 } |
|
1135 |
|
1136 void CBlockControl::StartL(const RMessage2& aMessage,const TDesC& aArgs, const TDesC8& aBlockArrayPckg) |
|
1137 { |
|
1138 if(IsActive()) |
|
1139 { |
|
1140 User::Leave(KErrInUse); |
|
1141 } |
|
1142 |
|
1143 Message() = aMessage; |
|
1144 Args().Copy(aArgs); |
|
1145 TBuf<8> heapSizeBuf(KNull); |
|
1146 TUint heapSize(0); |
|
1147 aMessage.ReadL(1,heapSizeBuf); |
|
1148 aMessage.Write(1,KNull); |
|
1149 TLex heapSizeLex; |
|
1150 |
|
1151 if (heapSizeBuf.Length() >=3) |
|
1152 { |
|
1153 if ( heapSizeBuf.Mid(0,2).CompareF(_L("0x")) == 0 ) |
|
1154 { |
|
1155 heapSizeLex.Assign(heapSizeBuf.Mid(2)); |
|
1156 } |
|
1157 else |
|
1158 { |
|
1159 heapSizeLex.Assign(heapSizeBuf); |
|
1160 } |
|
1161 heapSizeLex.Val(heapSize,EHex); |
|
1162 } |
|
1163 |
|
1164 // Set the BlockArray so it can be passed and used by the TestBlockController |
|
1165 CreateBlockArrayL( aBlockArrayPckg ); |
|
1166 |
|
1167 // Unique thread name guaranteed if we use the this pointer plus a random number |
|
1168 // whose seed was initialised to the address of the CTestServer object |
|
1169 TBuf<50> threadName; |
|
1170 _LIT(KWorker,"Worker%d %d"); |
|
1171 threadName.Format(KWorker,(TInt)this,Math::Rand(CONST_CAST(CTestServer&,Server()).RandSeed())); |
|
1172 |
|
1173 // Create with own heap so system cleans up if we kill it |
|
1174 if( (TInt)heapSize < KMinHeapSize ) |
|
1175 { |
|
1176 heapSize = KDefaultHeapSize; ///< Allow a 1Mb max heap |
|
1177 } |
|
1178 |
|
1179 User::LeaveIfError(WorkerThread().Create(threadName, ThreadBlockFunc, KDefaultStackSize + 0x1000,KMinHeapSize, heapSize, this, EOwnerProcess)); |
|
1180 |
|
1181 // Prime ready for completion |
|
1182 SetActive(); |
|
1183 // Use the appropriate variant call to get the thread exit |
|
1184 WorkerThread().Logon(iStatus); |
|
1185 WorkerThread().Resume(); |
|
1186 } |
|
1187 |
|
1188 void CBlockControl::Stop() |
|
1189 { |
|
1190 if(IsActive()) |
|
1191 { |
|
1192 WorkerThread().Kill(KErrAbort); |
|
1193 } |
|
1194 } |
|
1195 |
|
1196 CPersistentBlockControl::CPersistentBlockControl(CTestServer& aServer) |
|
1197 : CBlockControlBase(aServer) |
|
1198 , iWorkerControl(NULL) |
|
1199 , iWorkerMonitor(NULL) |
|
1200 , iInitialised(EFalse) |
|
1201 { |
|
1202 } |
|
1203 |
|
1204 CPersistentBlockControl::~CPersistentBlockControl() |
|
1205 { |
|
1206 |
|
1207 // Only need to clean up in the initialised state |
|
1208 if(!iInitialised) |
|
1209 return; |
|
1210 |
|
1211 // Check both objects |
|
1212 // Neither of them should be active, but just in case |
|
1213 if(iWorkerMonitor->IsActive()) |
|
1214 { |
|
1215 // Cancelling means we don't get stray events |
|
1216 WorkerThread().RendezvousCancel(iWorkerMonitor->Status()); |
|
1217 // Need to cancel the objeect itself |
|
1218 iWorkerMonitor->Cancel(); |
|
1219 } |
|
1220 if(iWorkerControl->IsActive()) |
|
1221 { |
|
1222 // Complete the request then cancel |
|
1223 TRequestStatus* status = &iWorkerControl->Status(); |
|
1224 User::RequestComplete(status,KErrNone); |
|
1225 iWorkerControl->Cancel(); |
|
1226 } |
|
1227 |
|
1228 // The worker thread will currently be blocked on its TRequestStatus at the top |
|
1229 // of its loop. |
|
1230 // Signal the status with KErrAbort and the thread will check this value and leave. |
|
1231 // If we Kill the thread then the cleanup stack for the thread is orphaned. |
|
1232 // PersistentThreadFuncL() TRAP's the leave. |
|
1233 // We logon and catch the thread exit. |
|
1234 TRequestStatus status = KRequestPending; |
|
1235 WorkerThread().Rendezvous(status); |
|
1236 TRequestStatus* workerStatus = &iWorkerControl->WorkerStatus(); |
|
1237 WorkerThread().RequestComplete(workerStatus,KErrAbort); |
|
1238 User::WaitForRequest(status); |
|
1239 // Close both handles |
|
1240 WorkerThread().Close(); |
|
1241 iWorkerControl->ControllerThread().Close(); |
|
1242 |
|
1243 delete iWorkerControl; |
|
1244 delete iWorkerMonitor; |
|
1245 } |
|
1246 |
|
1247 void PersistentThreadBlockFuncL(CBlockWorkerControl* aControl) |
|
1248 { |
|
1249 // Thread entry is sync'd with a semaphore |
|
1250 // Caller will Wait on this |
|
1251 // Also set our main sync treq to pending. |
|
1252 // It's completed to let us go in and execute the test step code. |
|
1253 aControl->WorkerStatus() = KRequestPending; |
|
1254 aControl->Semaphore().Signal(); |
|
1255 // Go into the main test step execution loop |
|
1256 for(;;) |
|
1257 { |
|
1258 User::WaitForRequest(aControl->WorkerStatus()); |
|
1259 // Check |
|
1260 if(aControl->WorkerStatus().Int() == KErrAbort) |
|
1261 User::Leave(KErrAbort); |
|
1262 CTestBlockController* block = REINTERPRET_CAST(CTestServer2&, aControl->Server()).CreateTestBlock(); |
|
1263 if(!block) |
|
1264 User::Leave(KErrNotFound); |
|
1265 CleanupStack::PushL(block); |
|
1266 // Set up the step base class members |
|
1267 TBool sharedData = ETrue; |
|
1268 block->InitialiseL(aControl->Args(), aControl->Server().Name(), sharedData); |
|
1269 |
|
1270 block->SetBlockArray( aControl->BlockArray() ); |
|
1271 block->SetSharedData( REINTERPRET_CAST(CTestServer2*, &aControl->Server()) ); |
|
1272 |
|
1273 ThreadStepExecutionL(aControl, block); |
|
1274 |
|
1275 // Pick up the final result |
|
1276 // Set it in the controlling class |
|
1277 aControl->Result() = block->TestStepResult(); |
|
1278 TBuf<KMaxTestExecuteNameLength> lError; |
|
1279 if (block->TestStepError() != 0) |
|
1280 lError.Num(block->TestStepError()); |
|
1281 if (lError != KNull) |
|
1282 { |
|
1283 lError.Insert(0,KErrorEquals); |
|
1284 aControl->PersistentError().Copy(lError); |
|
1285 } |
|
1286 CleanupStack::PopAndDestroy(block); |
|
1287 // Set our status for the wait at the top of the loop |
|
1288 aControl->WorkerStatus() = KRequestPending; |
|
1289 // Signal the status that our creator will be waiting on |
|
1290 // Creator's thread handle in the control class |
|
1291 TRequestStatus* status = &aControl->Status(); |
|
1292 aControl->ControllerThread().RequestComplete(status,KErrNone); |
|
1293 } |
|
1294 } |
|
1295 |
|
1296 TInt PersistentThreadBlockFunc(TAny* aParam) |
|
1297 { |
|
1298 // Create the thread's cleanup stack |
|
1299 CTrapCleanup* cleanup = CTrapCleanup::New(); |
|
1300 if(!cleanup) |
|
1301 return KErrNoMemory; |
|
1302 // Trap it and return the error code to the OS |
|
1303 TRAPD(err, PersistentThreadBlockFuncL(REINTERPRET_CAST(CBlockWorkerControl*,aParam))); |
|
1304 SytemWideErrToTefErr(err); |
|
1305 delete cleanup; |
|
1306 cleanup = NULL; |
|
1307 return err; |
|
1308 } |
|
1309 |
|
1310 void CPersistentBlockControl::StartL(const RMessage2& aMessage,const TDesC& aStepArgs, const TDesC8& aBlockArrayPckg) |
|
1311 { |
|
1312 if(IsActive()) |
|
1313 User::Leave(KErrInUse); |
|
1314 Message() = aMessage; |
|
1315 Args().Copy(aStepArgs); |
|
1316 |
|
1317 // Set the BlockArray so it can be passed and used by the TestBlockController |
|
1318 // Set the BlockArray so it can be passed and used by the TestBlockController |
|
1319 CreateBlockArrayL( aBlockArrayPckg ); |
|
1320 |
|
1321 // Check to see if we're reusing the worker thread and classes |
|
1322 if(!iInitialised) |
|
1323 { |
|
1324 // Need to construct the monitor and controller classes |
|
1325 // They are both constructed with a reference to our iStatus |
|
1326 // Either of them can complete us. We check their Active flags in our RunL() |
|
1327 iWorkerControl = new (ELeave) CBlockWorkerControl(Server(),iStatus); |
|
1328 // The worker thread needs our thread handle to RequestComplete us |
|
1329 User::LeaveIfError(iWorkerControl->ControllerThread().Duplicate(RThread())); |
|
1330 // Worker thread entry is sync'd with a semaphore. |
|
1331 User::LeaveIfError(iWorkerControl->Semaphore().CreateLocal(0)); |
|
1332 TBuf<50> threadName; |
|
1333 // Unique thread name guaranteed if we use the this pointer plus a random number |
|
1334 // whose seed was initialised to the address of the CTestServer object |
|
1335 // Create in our heap. |
|
1336 _LIT(KWorker,"Worker%d %d"); |
|
1337 threadName.Format(KWorker,(TInt)this,Math::Rand(CONST_CAST(CTestServer&,Server()).RandSeed())); |
|
1338 User::LeaveIfError(WorkerThread().Create(threadName,PersistentThreadBlockFunc, KDefaultStackSize + 0x1000,NULL,iWorkerControl, EOwnerProcess)); |
|
1339 iWorkerMonitor = new (ELeave) CWorkerMonitor(iStatus); |
|
1340 } |
|
1341 |
|
1342 // Set the Block Array |
|
1343 iWorkerControl->SetBlockArray( BlockArray() ); |
|
1344 |
|
1345 // Worker thread needs the step arguments and the step name |
|
1346 iWorkerControl->Args().Set(Args()); |
|
1347 |
|
1348 // Set this object ready for completion by either the monitor or controller objects |
|
1349 Prime(); |
|
1350 // Set the child monitor and control objects ready for completion |
|
1351 iWorkerMonitor->SetActive(); |
|
1352 iWorkerControl->Prime(); |
|
1353 // Use the monitor object to pick up thread exit |
|
1354 // This should only happen for panic, leave and abort following the Stop() call |
|
1355 WorkerThread().Rendezvous(iWorkerMonitor->Status()); |
|
1356 if(!iInitialised) |
|
1357 { |
|
1358 // Start the thread and sync up via the semaphore |
|
1359 WorkerThread().Resume(); |
|
1360 iWorkerControl->Semaphore().Wait(); |
|
1361 iWorkerControl->Semaphore().Close(); |
|
1362 iInitialised = ETrue; |
|
1363 } |
|
1364 // Worker thread will be at the top of its loop waiting to execute |
|
1365 // the test step virtuals. |
|
1366 // Issue the request then it will drop through |
|
1367 TRequestStatus* status = &iWorkerControl->WorkerStatus(); |
|
1368 WorkerThread().RequestComplete(status,KErrNone); |
|
1369 } |
|
1370 |
|
1371 void CPersistentBlockControl::RunL() |
|
1372 { |
|
1373 // Write back the test block |
|
1374 HBufC8* blockArrayPckg = CreateBlockArrayPckgLC(); |
|
1375 TPtr8 blockArrayPtr(blockArrayPckg->Des()); |
|
1376 Message().WriteL( 2, blockArrayPtr ); |
|
1377 CleanupStack::PopAndDestroy(blockArrayPckg); |
|
1378 |
|
1379 if (iWorkerControl->PersistentError() != KNull) |
|
1380 { |
|
1381 TBuf<KMaxTestExecuteNameLength> errorParam; |
|
1382 errorParam.Copy(iWorkerControl->PersistentError()); // Error Value returned as Panic Result |
|
1383 Message().Write(1,errorParam); |
|
1384 iWorkerControl->PersistentError().Copy(KNull); |
|
1385 } |
|
1386 TInt ret = KErrNone; |
|
1387 // Check which of the child objects completed us |
|
1388 if(!iWorkerMonitor->IsActive()) |
|
1389 { |
|
1390 // Unexpected exit from the worker thread |
|
1391 iInitialised = EFalse;// this also make ~CPersistentBlockControl not to delete twice. |
|
1392 // Pick up the exit reason and panic code if it exists |
|
1393 if(WorkerThread().ExitType() == EExitPanic) |
|
1394 { |
|
1395 TBuf<KMaxTestExecuteNameLength> panicParam(KPanicEquals); |
|
1396 TPtrC panicCat = WorkerThread().ExitCategory(); // Panic Value returned as Result |
|
1397 if( panicCat.Length() == 0 ) |
|
1398 { |
|
1399 panicParam.Append(_L("NULL")); |
|
1400 } |
|
1401 panicParam.Append( panicCat ); |
|
1402 Message().Write(1,panicParam); |
|
1403 } |
|
1404 |
|
1405 ret = WorkerThread().ExitReason(); |
|
1406 |
|
1407 if (WorkerThread().ExitType() == EExitPanic) |
|
1408 { |
|
1409 SytemWideErrToTefErr(ret); |
|
1410 } |
|
1411 if (ret == KErrAbort && iWorkerControl->TimedOut()) |
|
1412 { |
|
1413 if (Server().LoggerStarted()) |
|
1414 { |
|
1415 Server().ERR_PRINTF1(_L("TEST IS ABOUT TO ABORT DUE TO TEF TIMEOUT")); |
|
1416 } |
|
1417 } |
|
1418 |
|
1419 // We need to complete and cancel the other request so we don't have stray events |
|
1420 TRequestStatus* status = &iWorkerControl->Status(); |
|
1421 User::RequestComplete(status,KErrNone); |
|
1422 iWorkerControl->Cancel(); |
|
1423 // Free the resource in the worker control object |
|
1424 iWorkerControl->ControllerThread().Close(); |
|
1425 WorkerThread().Close(); |
|
1426 delete iWorkerControl; |
|
1427 iWorkerControl = NULL; |
|
1428 delete iWorkerMonitor; |
|
1429 iWorkerMonitor = NULL; |
|
1430 // Next time in to StartL() we create them from cleana |
|
1431 } |
|
1432 else if(!iWorkerControl->IsActive()) |
|
1433 { |
|
1434 // Normal test step completion |
|
1435 // We can reuse the thread next time into StartL() |
|
1436 // The thread will be blocking on iWorkerStatus |
|
1437 // We need to cancel the other object |
|
1438 WorkerThread().RendezvousCancel(iWorkerMonitor->Status()); |
|
1439 iWorkerMonitor->Cancel(); |
|
1440 // Retrieve the test result |
|
1441 ret = iWorkerControl->Result(); |
|
1442 SytemWideErrToTefErr(ret); |
|
1443 } |
|
1444 else |
|
1445 // Unexpected |
|
1446 { |
|
1447 ret = iStatus.Int(); |
|
1448 } |
|
1449 // Complete back to the client |
|
1450 Message().Complete(ret); |
|
1451 } |
|
1452 |
|
1453 void CPersistentBlockControl::Stop() |
|
1454 { |
|
1455 if(iWorkerMonitor->IsActive()) |
|
1456 { |
|
1457 WorkerThread().Kill(KErrAbort); |
|
1458 } |
|
1459 } |
|
1460 |
|
1461 /** |
|
1462 * //> @internalComponent |
|
1463 * @param aErr - Reference to the error number |
|
1464 * this delling with Leave or set result error number conflict with sys wide numbers[-1,-49] |
|
1465 */ |
|
1466 void SytemWideErrToTefErr(TInt &aErr) |
|
1467 { |
|
1468 switch (aErr) |
|
1469 { |
|
1470 case KErrInUse: |
|
1471 { |
|
1472 aErr = KErrTestExecuteInUse; |
|
1473 } |
|
1474 break; |
|
1475 case KErrServerBusy: |
|
1476 { |
|
1477 aErr = KErrTestExecuteServerBusy; |
|
1478 } |
|
1479 break; |
|
1480 }; |
|
1481 return; |
|
1482 } |