summaryrefslogtreecommitdiffstats
path: root/plugins/contacts/symbian/contactsmodel/tsrc/cntmodel2/t_requeststoretest.cpp
blob: 447bc6d1e08cd8dc1078d7ed42d8e79d03e16dbd (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
/*
* Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
* Contact: http://www.qt-project.org/legal
* This component and the accompanying materials are made available
* under the terms of "Eclipse Public License v1.0"
* which accompanies this distribution, and is available
* at the URL "http://www.eclipse.org/legal/epl-v10.html".
*
* Initial Contributors:
* Nokia Corporation - initial contribution.
*
* Contributors:
*
* Description: 
*
*/


/**
@SYMTestCaseID          PIM-APPENG-CNTMODEL-CNTSRV-CT-106277-0001
@SYMTestCaseDesc        Request store testing.  Ensure the request store can handle failed requests.
@SYMTestType            CIT
@SYMTestPriority        High
@SYMTestActions         1. Create thread 1 signaling a start of transaction to lock the 
                           default database. 
                        2. Create thread 2 to continuously sends a large number of invalid delete 
                           requests to the database.
                        3. After a specific amount of time elapsed, signaling a commit from thread 1
                           to release the database.                          
@SYMTestExpectedResults Test should completed without error or panic.
@SYMTestStatus          Implemented
@SYMDEF                 DEF106277 - CntModel - CRequestStore attempts to double-delete a CCntRequest

Note: T_RequestStoreTest is created base on T_DBTransactionTest with minor modifications to suit 
      this test.  
*/

#include <e32base.h>

#include "nbcnttestlib.h"
#include "t_requeststoretest.h"

_LIT(KTestName, "T_RequestStoreTest"); // Used by Testhelper - gives an warning under ARMv5

#include "testhelpers.h"

// Adjust KNumContacts to allow pre-population of contact database if
// pre-existing contact items are needed in the database.
const TInt KNumContacts = 10;

// Number of delete request to be made 
const TInt KDeleteRequest = 500;

// adjust KLockPeriod to ensure there's enough lock-down time
// to force requests from other threads be stored in the Request
// store.
const TInt KLockPeriod = 10000000;

//===========================================================
// CLockDatabase Class
//===========================================================

CLockDatabase* CLockDatabase::NewLC()
    {
    CLockDatabase* self = new (ELeave) CLockDatabase();
    CleanupStack::PushL(self);
    self->ConstructL();
    return self;
    }

CLockDatabase::~CLockDatabase()
    {
    if (iContactDatabase)
        delete iContactDatabase;
    iTest->End();
    iTest->Close();
    delete iTest;
    }

CLockDatabase::CLockDatabase()
    : CActive(EPriorityIdle)
    {
    CActiveScheduler::Add(this);
    }

void CLockDatabase::ConstructL()
    {
    TPtrC name(RThread().Name());
    iTest = new(ELeave) RTest(name);
    iTest->Start(_L("T_LockDatabase starts ..."));
    OpenDatabaseL();
    }

void CLockDatabase::OpenDatabaseL()
    {
    iContactDatabase = CContactDatabase::OpenL(CContactDatabase::EMultiThread);

    TRequestStatus *pS = &iStatus;
    User::RequestComplete(pS, KErrNone);
    SetActive();
    CActiveScheduler::Start();
    }

void CLockDatabase::DoCancel()
    {
    CActiveScheduler::Stop();
    }

TInt CLockDatabase::RunError(TInt aError)
    {
    return aError;
    }

void CLockDatabase::RunL()
    {
    if (!iLocked)
        {
        iTest->Printf(_L("T_LockDatabase - Lock database ..."));
        iContactDatabase->DatabaseBeginL(EFalse);

        // When requests are sent to the Contact database server during transaction(lock-down)
        // state, all the requests sent from other threads are being held in the Request Store
        // waiting to be processed upon the very next change of state.
        //
        // Therefore, in order to ensure the request store gets any request, ensure there's
        // enough lock-down time before unlocking the contact database again.
        User::After(KLockPeriod);

        TRequestStatus *pS = &iStatus;
        User::RequestComplete(pS, KErrNone);
        SetActive();
        iLocked = ETrue;
        }
    else
        {
        iTest->Printf(_L("T_LockDatabase - Unlock database ..."));
        iContactDatabase->DatabaseCommitL(ETrue);
        iLocked = EFalse;
        CActiveScheduler::Stop();
        }
    }


//===========================================================
// CDeleteInvalidCnt Class
//===========================================================

CDeleteInvalidCnt* CDeleteInvalidCnt::NewLC()
    {
    CDeleteInvalidCnt* self = new (ELeave) CDeleteInvalidCnt();
    CleanupStack::PushL(self);
    self->ConstructL();
    return self;
    }

CDeleteInvalidCnt::~CDeleteInvalidCnt()
    {
    if (iContactDatabase)
        delete iContactDatabase;
    iTest->End();
    iTest->Close();
    delete iTest;
    }

CDeleteInvalidCnt::CDeleteInvalidCnt()
    : CActive(EPriorityIdle),
      iStep(-1)
    {
    CActiveScheduler::Add(this);
    }

void CDeleteInvalidCnt::ConstructL()
    {
    TPtrC name(RThread().Name());
    iTest = new(ELeave) RTest(name);
    iTest->Start(_L("T_DeleteInvalidCnt starts"));

    OpenDatabaseL();
    }

void CDeleteInvalidCnt::OpenDatabaseL()
    {
    iContactDatabase = CContactDatabase::OpenL(CContactDatabase::EMultiThread);

    iStep++;
    iTest->Printf(_L("T_DeleteInvalidCnt - Proceed to CContactDatabase::DeleteContactL() in bulk..."));

    TRequestStatus *pS = &iStatus;
    User::RequestComplete(pS, KErrNone);
    SetActive();
    CActiveScheduler::Start();
    }

void CDeleteInvalidCnt::DoCancel()
    {
    CActiveScheduler::Stop();
    }

TInt CDeleteInvalidCnt::RunError(TInt aError)
    {
    return aError;
    }

void CDeleteInvalidCnt::RunL()
    {
    if (iStep < KDeleteRequest)
        {
        // always delete a non-existent contact item to force a failure
        TContactItemId invalidCnt = iStep + iContactDatabase->CountL() + 1;
        TRAP_IGNORE(iContactDatabase->DeleteContactL(invalidCnt));
        /*
        CContactItem* item = NULL;
        TRAP_IGNORE(item = iContactDatabase->OpenContactL(1));
        if (item)
            delete item;        */
        
        iTest->Printf(_L("T_DeleteInvalidCnt - Issued request CContactDatabase::DeleteContactL(%d)..."),
                      invalidCnt);
//        User::After(200000);
        iStep++;

        TRequestStatus *pS = &iStatus;
        User::RequestComplete(pS, KErrNone);
        SetActive();
        }
    else
        {
        iTest->Printf(_L("T_DeleteInvalidCnt - All requests completed..."));
        CActiveScheduler::Stop();
        }
    }


//===========================================================
// CConcurrentController Class
//===========================================================

CConcurrentController::~CConcurrentController()
    {
    iStore.ResetAndDestroy();
    }

CConcurrentController* CConcurrentController::NewLC()
    {
    CConcurrentController* self = new (ELeave) CConcurrentController();
    CleanupStack::PushL(self);
    return self;
    }

CConcurrentController::CConcurrentController()
    {}

void CConcurrentController::StartTestL()
    {
    // Create some entries into the contact database for use
    CreateContactsL(KNumContacts);

    // test to verify the RequestStore is properly handling a failed
    // request
    StartRequestStoreFailureTestL();

    // In the future, more tests should be added to verify the handling of
    // requests within the Request Store under different situations
    // 
    // In order to add new tests:
    // 
    // 1. Use CLockDatabase as the thread to force requests to be stored
    //    within the Request Store
    // 2. Create a new class(es) using CDeleteInvalidCnt as a template for 
    //    send requests which satisfy the desired test condition (eg.
    //    leave, panic, success, timeout)
    // 3. Create a new function using StartRequestStoreFailureTestL to
    //    trigger the threads to run
    // 4. Add the new function in Step 3 here to include it as one of the 
    //    the Request Store test 
    }

void CConcurrentController::StartRequestStoreFailureTestL()
    {
    test.Printf(_L("Starting Request Store Failure Test ..."));

    CreateTestersL(2);

    // Create a thread for locking the database while the other
    // thread attempts to delete contact items that does not exists

    // Thread 1. Start sending delete requests which are guaranteed to fail
    iStore[0]->RunTestThreadL(EDeleteInvalidCnt);

    // Thread 2. Trigger a lock-down of the database and unlock it after a while
    iStore[1]->RunTestThreadL(ELockDatabase);

    // While Thread 1 continues to send delete requests, the database server
    // will be locked down temporily by Thread 2.  During lock-down time,
    // the database server will store all incoming requests, from a different thread
    // compare to the one locking down the server, into the request store.  After a
    // specific time elapsed, Thread 2 will unlock the database, causing a state change
    // in the database server.  Upon this very first state change, all requests stored
    // in the Request Store will be retrieved and processed within the store's own
    // Active Loop and they will fail.
    while (!Completed())
        User::WaitForAnyRequest();
    }

void CConcurrentController::CreateTestersL(TInt aNoOfTesters)
    {
    // flush all pre-existing CConcurrentTester instances
    iStore.ResetAndDestroy();

    if (iStore.Count() == 0)
        {
        _LIT(KThreadName,"Thread_%d");
        TBuf<256> buf;

        for (TInt ii = 0; ii < aNoOfTesters; ++ii)
            {
            buf.Format(KThreadName, ii);
            iStore.AppendL(CConcurrentTester::NewL(CEventResponse::NewL(*this), buf));
            }
        }
    }

TBool CConcurrentController::Completed()
    {
    TInt noOfTesters = iStore.Count();
    for (TInt ii = 0; ii < noOfTesters; ++ii)
        {
        if (iStore[ii]->IsCompleted())
            return EFalse;
        }
    return ETrue;
    }

void CConcurrentController::PublishError(TInt aError)
    {
    test.Printf(_L(" ->  TEST FAILED - Error %d was reported during execution"), aError);
    }

void CConcurrentController::CreateContactsL(TInt aNumContacts)
    {
    // flush all existing entries, and create a blank new one
    CContactDatabase* db = CContactDatabase::ReplaceL();
    CleanupStack::PushL(db);

    CContactItemViewDef* matchAll
        = CContactItemViewDef::NewLC(CContactItemViewDef::EIncludeFields,
                                     CContactItemViewDef::EIncludeHiddenFields);
    matchAll->AddL(KUidContactFieldMatchAll);

    CContactItem* item = db->ReadContactL(db->TemplateId(), *matchAll);

    CleanupStack::PopAndDestroy(matchAll);

    CContactTemplate* cntTemplate = static_cast<CContactTemplate*>(item);
    CleanupStack::PushL(cntTemplate);

    CCntItemBuilder* builder = CCntItemBuilder::NewLC(*cntTemplate);

    for (TInt i = 0; i < aNumContacts; i++)
        {
        // Create the contact
        CContactItem* cntItem = builder->GetCntItemLC();

        TRAP_IGNORE(db->AddNewContactL(*cntItem));

        CleanupStack::PopAndDestroy(cntItem);
        }

    CleanupStack::PopAndDestroy(builder);
    CleanupStack::PopAndDestroy(cntTemplate);
    CleanupStack::PopAndDestroy(db);
    }

//===========================================================
// CEventResponse Class
//===========================================================

CEventResponse* CEventResponse::NewL(CConcurrentController& aController)
    {
    CEventResponse* self = new (ELeave) CEventResponse(aController);
    return self;
    }


CEventResponse::CEventResponse(CConcurrentController& aController) : CActive(EPriorityIdle), iController(aController)
    {
    CActiveScheduler::Add(this);
    }


CEventResponse::~CEventResponse()
    {}

void CEventResponse::RunL()
    {
    test.Next(_L("-> Thread Finished"));

    }

void CEventResponse::DoCancel()
    {
    Cancel();
    }

TInt CEventResponse::RunError(TInt aError)
    {
    iController.PublishError(aError);
    return aError;
    }

void CEventResponse::CompleteRequest(TInt aError)
    {
    TRequestStatus* status = &iStatus;
    User::RequestComplete(status, aError);
    }

TRequestStatus& CEventResponse::RequestStatus()
    {
    return iStatus;
    }


//===========================================================
// CConcurrentTester Class
//===========================================================

CConcurrentTester::~CConcurrentTester()
    {
    delete iResponse;
    iThread.Close();
    }

CConcurrentTester* CConcurrentTester::NewL(CEventResponse* aResponse, const TDesC& aThreadName)
    {
    CConcurrentTester* self = new (ELeave) CConcurrentTester(aResponse, aThreadName);
    return self;
    }

TInt CConcurrentTester::ThreadFunction(TAny* aTester)
    {
    CTrapCleanup* cleanup = CTrapCleanup::New();
    if (!cleanup)
    	{
        return KErrNoMemory;
		}

    CActiveScheduler*  scheduler = new CActiveScheduler;
    CActiveScheduler::Install(scheduler);

    TRAPD(err, static_cast<CConcurrentTester*>(aTester)->RunTestL());

    delete scheduler;
    scheduler = NULL;
    delete cleanup;
    cleanup   = NULL;

    return err;
    }

TBool CConcurrentTester::IsCompleted()
    {
    return (iResponse->RequestStatus() == KRequestPending);
    }

// Wrapper method for Test class RunTestL()
// This method is called from within the ThreadFunction() method
// and runs in a new thread. Apart from this function all the concurrent
// logic is in the CTransactionThreadTest class.

void CConcurrentTester::RunTestL()
    {
    switch(iTestCode)
        {
        case ELockDatabase:
            {
            CLockDatabase* lockTest = CLockDatabase::NewLC();
            CleanupStack::PopAndDestroy(lockTest);
            lockTest = NULL;
            break;
            }
        case EDeleteInvalidCnt:
            {
            CDeleteInvalidCnt* deleteTest = CDeleteInvalidCnt::NewLC();
            CleanupStack::PopAndDestroy(deleteTest);
            deleteTest = NULL;
            break;
            }
        default:
            {
            User::Leave(KErrNotFound);
            }
        }
    }


void CConcurrentTester::RunTestThreadL(TInt aTestCode)
    {
    test.Next(_L("-> %s Started"));


    iTestCode = aTestCode;
    User::LeaveIfError(iThread.Create(iThreadName,
                                      CConcurrentTester::ThreadFunction,
                                      KDefaultStackSize,
                                      0x2000,
                                      0x200000,
                                      this,
                                      EOwnerThread));

    iThread.SetPriority(EPriorityMuchLess);
    iThread.Logon (iResponse->RequestStatus());
    iThread.Resume();
    }
   

LOCAL_C void DoTestsL()
    {
    CleanupClosePushL(test);

    test.Start(_L("@SYMTESTCaseID:PIM-APPENG-CNTMODEL-CNTSRV-CT-106277-0001 ----------- Request Store tests begins ----------"));


    CConcurrentController* testController = CConcurrentController::NewLC();
    testController->StartTestL();

    test.Printf(_L (" ----------- Request Store tests ends ---------- "));
    test.End();

    CleanupStack::PopAndDestroy(2);     // testController & test
    }

GLDEF_C TInt E32Main()
    {
    __UHEAP_MARK;

    CActiveScheduler* scheduler=new CActiveScheduler;
    if (scheduler)
        {
        CActiveScheduler::Install(scheduler);
        CTrapCleanup* cleanup = CTrapCleanup::New();
        if (cleanup)
            {
            TRAPD(err,DoTestsL());
            __ASSERT_ALWAYS(err == KErrNone, User::Panic(_L("DBDumper Failed"),err) );
            delete cleanup;
            }
        delete scheduler;
        }

    __UHEAP_MARKEND;
    return KErrNone;
    }