|
1 // Copyright (c) 2001-2009 Nokia Corporation and/or its subsidiary(-ies). |
|
2 // All rights reserved. |
|
3 // This component and the accompanying materials are made available |
|
4 // under the terms of "Eclipse Public License v1.0" |
|
5 // which accompanies this distribution, and is available |
|
6 // at the URL "http://www.eclipse.org/legal/epl-v10.html". |
|
7 // |
|
8 // Initial Contributors: |
|
9 // Nokia Corporation - initial contribution. |
|
10 // |
|
11 // Contributors: |
|
12 // |
|
13 // Description: |
|
14 // |
|
15 |
|
16 #include <e32test.h> |
|
17 #include <cntdef.h> |
|
18 #include <cntdb.h> |
|
19 #include <cntitem.h> |
|
20 #include <cntfield.h> |
|
21 #include <cntfldst.h> |
|
22 #include <cntviewbase.h> |
|
23 #include <cntview.h> |
|
24 |
|
25 #include "cfixedqueue.h" |
|
26 #include "CContactViewEventQueue.h" |
|
27 |
|
28 _LIT(KTestName, "T_FilteredViewUpdate"); |
|
29 |
|
30 _LIT(KTestDbName, "c:T_FilteredViewUpdate.cdb"); |
|
31 |
|
32 LOCAL_D RTest test(KTestName); |
|
33 |
|
34 |
|
35 class CDbEventListener : public CTimer, public MContactDbObserver |
|
36 { |
|
37 public: |
|
38 static CDbEventListener* NewL |
|
39 (CContactDatabase& aDb, TInt aMaxQueueSize=128); |
|
40 ~CDbEventListener(); |
|
41 |
|
42 /** |
|
43 * Waits for an event from the database. |
|
44 * @param aTimeOut max time to wait for an event. |
|
45 * @param aEvent the received event, undefined if this function returns false. |
|
46 * @return true if an event was received, false if timeout expired first. |
|
47 */ |
|
48 TBool ListenForEvent(TTimeIntervalSeconds aTimeOut, TContactDbObserverEvent& aEvent); |
|
49 |
|
50 /** |
|
51 * Removes all previously arrvied events from the queue. |
|
52 */ |
|
53 void Flush() |
|
54 { |
|
55 iEventQueue.Reset(); |
|
56 } |
|
57 |
|
58 private: // from CTimer |
|
59 void RunL(); |
|
60 |
|
61 private: // from MContactDbObserver |
|
62 void HandleDatabaseEventL(TContactDbObserverEvent aEvent); |
|
63 |
|
64 private: // Implementation |
|
65 CDbEventListener(); |
|
66 void ConstructL(CContactDatabase& aDb, TInt aMaxQueueSize); |
|
67 |
|
68 private: // Data |
|
69 CContactChangeNotifier* iContactChangeNotifier; |
|
70 CFixedQueue<TContactDbObserverEvent> iEventQueue; |
|
71 }; |
|
72 |
|
73 CDbEventListener* CDbEventListener::NewL |
|
74 (CContactDatabase& aDb, TInt aMaxQueueSize/*=128*/) |
|
75 { |
|
76 CDbEventListener* self = new(ELeave) CDbEventListener; |
|
77 CleanupStack::PushL(self); |
|
78 self->ConstructL(aDb,aMaxQueueSize); |
|
79 CleanupStack::Pop(self); |
|
80 return self; |
|
81 } |
|
82 |
|
83 CDbEventListener::~CDbEventListener() |
|
84 { |
|
85 delete iContactChangeNotifier; |
|
86 CTimer::Cancel(); |
|
87 } |
|
88 |
|
89 TBool CDbEventListener::ListenForEvent(TTimeIntervalSeconds aTimeOut, TContactDbObserverEvent& aEvent) |
|
90 { |
|
91 CTimer::Cancel(); |
|
92 |
|
93 if (iEventQueue.IsEmpty()) |
|
94 { |
|
95 CTimer::After(aTimeOut.Int() * 1000000); |
|
96 // Keep execution here until the timer expires |
|
97 do { |
|
98 CActiveScheduler::Start(); |
|
99 } |
|
100 while (CTimer::IsActive()); |
|
101 } |
|
102 |
|
103 if (!iEventQueue.IsEmpty()) |
|
104 { |
|
105 aEvent = iEventQueue.Head(); |
|
106 iEventQueue.PopHead(); |
|
107 return ETrue; |
|
108 } |
|
109 else |
|
110 { |
|
111 return EFalse; |
|
112 } |
|
113 } |
|
114 |
|
115 void CDbEventListener::RunL() |
|
116 { |
|
117 // Timer expired |
|
118 CActiveScheduler::Stop(); |
|
119 } |
|
120 |
|
121 void CDbEventListener::HandleDatabaseEventL(TContactDbObserverEvent aEvent) |
|
122 { |
|
123 const TBool timerWasActive = CTimer::IsActive(); |
|
124 CTimer::Cancel(); |
|
125 TBool eventPushed = iEventQueue.Push(aEvent); |
|
126 __ASSERT_ALWAYS(eventPushed,User::Invariant()); |
|
127 if (timerWasActive) |
|
128 { |
|
129 CActiveScheduler::Stop(); |
|
130 } |
|
131 } |
|
132 |
|
133 CDbEventListener::CDbEventListener() |
|
134 : CTimer(CActive::EPriorityStandard) |
|
135 { |
|
136 } |
|
137 |
|
138 void CDbEventListener::ConstructL |
|
139 (CContactDatabase& aDb, TInt aMaxQueueSize) |
|
140 { |
|
141 CTimer::ConstructL(); |
|
142 CActiveScheduler::Add(this); |
|
143 iEventQueue.ConstructL(aMaxQueueSize); |
|
144 iContactChangeNotifier = CContactChangeNotifier::NewL(aDb, this); |
|
145 } |
|
146 |
|
147 class CTestResources : public CBase |
|
148 { |
|
149 public: |
|
150 static CTestResources* NewLC(); |
|
151 void ConstructL(); |
|
152 void CreateTestContactsL(); |
|
153 ~CTestResources(); |
|
154 |
|
155 static const TInt KTestContacts; |
|
156 CContactDatabase* iDb; |
|
157 CContactIdArray* iContacts; |
|
158 TContactItemId iGroupId; |
|
159 CDbEventListener* iDbEventQueue; |
|
160 CContactViewEventQueue* iViewEventQueue; |
|
161 RContactViewSortOrder iViewSortOrder; |
|
162 CContactRemoteView* iRemoteView; |
|
163 CContactViewEventQueue* iFilteredViewEventQueue; |
|
164 CContactFilteredView* iFilteredView; |
|
165 }; |
|
166 |
|
167 const TInt CTestResources::KTestContacts = 8; |
|
168 |
|
169 |
|
170 CTestResources* CTestResources::NewLC() |
|
171 { |
|
172 CTestResources* self = new(ELeave) CTestResources; |
|
173 CleanupStack::PushL(self); |
|
174 self->ConstructL(); |
|
175 return self; |
|
176 } |
|
177 |
|
178 void CTestResources::ConstructL() |
|
179 { |
|
180 iDb = CContactDatabase::ReplaceL(KTestDbName); |
|
181 iDbEventQueue = CDbEventListener::NewL(*iDb); |
|
182 |
|
183 CreateTestContactsL(); |
|
184 |
|
185 iViewEventQueue = CContactViewEventQueue::NewL(); |
|
186 |
|
187 iViewSortOrder.AppendL(KUidContactFieldFamilyName); |
|
188 iViewSortOrder.AppendL(KUidContactFieldGivenName); |
|
189 iViewSortOrder.AppendL(KUidContactFieldCompanyName); |
|
190 |
|
191 iRemoteView = CContactRemoteView::NewL |
|
192 (*iViewEventQueue, *iDb, iViewSortOrder, EContactsOnly); |
|
193 |
|
194 iFilteredViewEventQueue = CContactViewEventQueue::NewL(); |
|
195 iFilteredView = CContactFilteredView::NewL |
|
196 (*iFilteredViewEventQueue, *iDb, *iRemoteView, CContactDatabase::EMailable); |
|
197 |
|
198 // Wait for filtered view to get ready |
|
199 TContactViewEvent event; |
|
200 TBool eventReady = iFilteredViewEventQueue->ListenForEvent(10,event); |
|
201 __ASSERT_ALWAYS(eventReady,User::Invariant()); |
|
202 ASSERT(event.iEventType == TContactViewEvent::EReady); |
|
203 |
|
204 // Flush all other events |
|
205 iDbEventQueue->Flush(); |
|
206 iViewEventQueue->Flush(); |
|
207 iFilteredViewEventQueue->Flush(); |
|
208 } |
|
209 |
|
210 void CTestResources::CreateTestContactsL() |
|
211 { |
|
212 TInt i; |
|
213 iContacts = CContactIdArray::NewL(); |
|
214 |
|
215 // Create contacts |
|
216 for (i=1; i <= KTestContacts; ++i) |
|
217 { |
|
218 CContactCard* card = CContactCard::NewLC(); |
|
219 CContactItemField* field = CContactItemField::NewLC(KStorageTypeText, KUidContactFieldFamilyName); |
|
220 TBuf<30> name; |
|
221 name.Format(_L("Contact%02d"), i); |
|
222 field->TextStorage()->SetTextL(name); |
|
223 card->AddFieldL(*field); |
|
224 CleanupStack::Pop(field); |
|
225 if (i % 2 == 0) |
|
226 { |
|
227 // Add an email field to every other contact |
|
228 CContactItemField* field = CContactItemField::NewLC(KStorageTypeText, KUidContactFieldEMail); |
|
229 field->TextStorage()->SetTextL(_L("email")); |
|
230 card->AddFieldL(*field); |
|
231 CleanupStack::Pop(field); |
|
232 } |
|
233 const TContactItemId contactId = iDb->AddNewContactL(*card); |
|
234 iContacts->AddL(contactId); |
|
235 CleanupStack::PopAndDestroy(card); |
|
236 |
|
237 // Eat away contact db events |
|
238 TContactDbObserverEvent event; |
|
239 do |
|
240 { |
|
241 TBool eventReady = iDbEventQueue->ListenForEvent(10,event); |
|
242 __ASSERT_ALWAYS(eventReady,User::Invariant()); |
|
243 } |
|
244 while (event.iType != EContactDbObserverEventContactAdded || |
|
245 event.iContactId != contactId); |
|
246 } |
|
247 } |
|
248 |
|
249 CTestResources::~CTestResources() |
|
250 { |
|
251 if (iFilteredView) iFilteredView->Close(*iFilteredViewEventQueue); |
|
252 delete iFilteredViewEventQueue; |
|
253 if (iRemoteView) iRemoteView->Close(*iViewEventQueue); |
|
254 iViewSortOrder.Close(); |
|
255 delete iViewEventQueue; |
|
256 delete iDbEventQueue; |
|
257 delete iContacts; |
|
258 delete iDb; |
|
259 TRAP_IGNORE(CContactDatabase::DeleteDatabaseL(KTestDbName)); |
|
260 } |
|
261 |
|
262 LOCAL_C void CheckConsistentL |
|
263 (CContactDatabase& aDb, |
|
264 const CContactFilteredView& aFilteredView, |
|
265 const CContactViewBase& aBaseView, |
|
266 TFieldType aFieldType) |
|
267 { |
|
268 const TInt filteredViewCount = aFilteredView.CountL(); |
|
269 test(filteredViewCount <= aBaseView.CountL()); |
|
270 |
|
271 // Check that filtered view contains all the contacts it should |
|
272 // from the base view |
|
273 const TInt count = aBaseView.CountL(); |
|
274 TInt i; |
|
275 for (i=0; i < count; ++i) |
|
276 { |
|
277 const TContactItemId id = aBaseView.AtL(i); |
|
278 CContactItem* item = aDb.ReadContactLC(id); |
|
279 if (item->CardFields().Find(aFieldType) != KErrNotFound) |
|
280 { |
|
281 TInt pos = aFilteredView.FindL(id); |
|
282 test(pos != KErrNotFound); |
|
283 } |
|
284 CleanupStack::PopAndDestroy(item); |
|
285 } |
|
286 |
|
287 // Check that filtered view doesn't contain any contacts it shouldn't contain |
|
288 for (i=0; i < filteredViewCount; ++i) |
|
289 { |
|
290 const TContactItemId id = aFilteredView.AtL(i); |
|
291 CContactItem* item = aDb.ReadContactLC(id); |
|
292 test(item->CardFields().Find(aFieldType) != KErrNotFound); |
|
293 CleanupStack::PopAndDestroy(item); |
|
294 } |
|
295 } |
|
296 |
|
297 LOCAL_C void CheckConsistentL(CTestResources& aRes) |
|
298 { |
|
299 CheckConsistentL(*aRes.iDb, *aRes.iFilteredView, *aRes.iRemoteView, KUidContactFieldEMail); |
|
300 } |
|
301 |
|
302 // Removes the last contact with an email address from the DB. The contact happens |
|
303 // also to be the last contact in the underlying view |
|
304 // |
|
305 // This test crashes because the filtered view accesses its underlying view with an |
|
306 // out of bounds index. |
|
307 |
|
308 LOCAL_C void TestDeleteLastEmailContactL() |
|
309 { |
|
310 test.Next(_L("Delete last email contact")); |
|
311 |
|
312 |
|
313 CTestResources* res = CTestResources::NewLC(); |
|
314 CheckConsistentL(*res); |
|
315 |
|
316 const TInt lastEmailContactIndex = res->iFilteredView->CountL() - 1; |
|
317 const TContactItemId lastEmailContactId = res->iFilteredView->AtL(lastEmailContactIndex); |
|
318 |
|
319 res->iDb->DeleteContactL(lastEmailContactId); |
|
320 |
|
321 // Wait for the update event from the filtered view |
|
322 TContactViewEvent event; |
|
323 test(res->iFilteredViewEventQueue->ListenForEvent(10,event)); |
|
324 test(event.iEventType==TContactViewEvent::EItemRemoved && event.iInt==lastEmailContactIndex && event.iContactId==lastEmailContactId); |
|
325 CleanupStack::PopAndDestroy(res); |
|
326 } |
|
327 |
|
328 // Renames last of the email contacts so that it will move to the beginning of |
|
329 // sort order. |
|
330 // |
|
331 // This test fails because CContactFilteredView::HandleRemoveEventL messes up the removal |
|
332 // and sends back an event with incorrect information. With some luck, however, the filtered |
|
333 // view is consistent after the EItemAdded event arrives. |
|
334 |
|
335 |
|
336 LOCAL_C void TestRenameLastEmailContactL() |
|
337 { |
|
338 test.Next(_L("Rename last email contact")); |
|
339 |
|
340 |
|
341 CTestResources* res = CTestResources::NewLC(); |
|
342 CheckConsistentL(*res); |
|
343 |
|
344 const TInt lastEmailContactIndex = res->iFilteredView->CountL() - 1; |
|
345 const TContactItemId lastEmailContactId = res->iFilteredView->AtL(lastEmailContactIndex); |
|
346 CContactItem* item = res->iDb->OpenContactLX(lastEmailContactId); |
|
347 CleanupStack::PushL(item); |
|
348 CContactItemField& nameField = item->CardFields()[item->CardFields().Find(KUidContactFieldFamilyName)]; |
|
349 nameField.TextStorage()->SetTextL(_L("AAAFirst")); |
|
350 res->iDb->CommitContactL(*item); |
|
351 CleanupStack::PopAndDestroy(2); |
|
352 |
|
353 // Wait for the update events from the filtered view |
|
354 TContactViewEvent event; |
|
355 |
|
356 test(res->iFilteredViewEventQueue->ListenForEvent(10,event)); |
|
357 test(event.iEventType==TContactViewEvent::EItemRemoved && event.iInt==lastEmailContactIndex && event.iContactId==lastEmailContactId); |
|
358 |
|
359 test(res->iFilteredViewEventQueue->ListenForEvent(10,event)); |
|
360 test(event.iEventType==TContactViewEvent::EItemAdded && event.iInt==0 && event.iContactId==lastEmailContactId); |
|
361 |
|
362 // Check consistency |
|
363 CheckConsistentL(*res); |
|
364 |
|
365 CleanupStack::PopAndDestroy(res); |
|
366 } |
|
367 |
|
368 LOCAL_C void TestChangeContactL() |
|
369 { |
|
370 test.Next(_L("Commit and delete")); |
|
371 |
|
372 |
|
373 CTestResources* res = CTestResources::NewLC(); |
|
374 CheckConsistentL(*res); |
|
375 |
|
376 // step1 - Open a contact |
|
377 const TInt lastEmailContactIndex = res->iFilteredView->CountL() - 1; |
|
378 const TContactItemId lastEmailContactId = res->iFilteredView->AtL(lastEmailContactIndex); |
|
379 CContactItem* item = res->iDb->OpenContactLX(lastEmailContactId); |
|
380 CleanupStack::PushL(item); |
|
381 // step2 - Edit a contact field |
|
382 CContactItemField& nameField = item->CardFields()[item->CardFields().Find(KUidContactFieldFamilyName)]; |
|
383 nameField.TextStorage()->SetTextL(_L("AAAFirst")); |
|
384 // step3 - Commit the contact (remove and add in one time) |
|
385 res->iDb->CommitContactL(*item); |
|
386 // step4 - Delete the new contact |
|
387 res->iDb->CloseContactL(lastEmailContactId); |
|
388 res->iDb->DeleteContactL(lastEmailContactId); |
|
389 |
|
390 CleanupStack::PopAndDestroy(2); |
|
391 |
|
392 // Wait for the remove event from the filtered view |
|
393 TContactViewEvent event; |
|
394 |
|
395 test(res->iFilteredViewEventQueue->ListenForEvent(10,event)); |
|
396 test(event.iEventType==TContactViewEvent::EItemRemoved && event.iInt==lastEmailContactIndex && event.iContactId==lastEmailContactId); |
|
397 |
|
398 // Check there are no more events |
|
399 test(!res->iFilteredViewEventQueue->ListenForEvent(10,event)); |
|
400 // Check consistency |
|
401 CheckConsistentL(*res); |
|
402 CleanupStack::PopAndDestroy(res); |
|
403 } |
|
404 |
|
405 /** |
|
406 |
|
407 @SYMTestCaseID PIM-T-FILTEREDVIEWUPDATE-0001 |
|
408 |
|
409 */ |
|
410 |
|
411 LOCAL_C void DoTestsL() |
|
412 { |
|
413 test.Start(_L("@SYMTESTCaseID:PIM-T-FILTEREDVIEWUPDATE-0001 T_FilteredViewUpdate")); |
|
414 |
|
415 TestDeleteLastEmailContactL(); |
|
416 TestRenameLastEmailContactL(); |
|
417 TestChangeContactL(); |
|
418 test.End(); |
|
419 test.Close(); |
|
420 |
|
421 } |
|
422 |
|
423 GLDEF_C TInt E32Main() |
|
424 { |
|
425 // Init |
|
426 __UHEAP_MARK; |
|
427 |
|
428 CTrapCleanup* cleanupStack = CTrapCleanup::New(); |
|
429 if (!cleanupStack) |
|
430 { |
|
431 return KErrNoMemory; |
|
432 } |
|
433 |
|
434 CActiveScheduler* activeScheduler = new CActiveScheduler; |
|
435 if (!activeScheduler) |
|
436 { |
|
437 return KErrNoMemory; |
|
438 } |
|
439 CActiveScheduler::Install(activeScheduler); |
|
440 |
|
441 // Run the tests |
|
442 TRAPD(err, DoTestsL()); |
|
443 __ASSERT_ALWAYS(err==KErrNone,User::Invariant()); |
|
444 |
|
445 // Cleanup |
|
446 delete activeScheduler; |
|
447 delete cleanupStack; |
|
448 |
|
449 __UHEAP_MARKEND; |
|
450 |
|
451 return err; |
|
452 } |