|
1 // Copyright (c) 2008-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 <bautils.h> |
|
18 #include <sqldb.h> |
|
19 #include <hal.h> |
|
20 #include "SqlUtil.h" |
|
21 |
|
22 /////////////////////////////////////////////////////////////////////////////////////// |
|
23 |
|
24 RTest TheTest(_L("t_sqlcompact1 test")); |
|
25 |
|
26 RSqlDatabase TheDb; |
|
27 |
|
28 const TInt KTextLen = 400; |
|
29 TBuf<KTextLen> TheText; |
|
30 TBuf<KTextLen + 100> TheSqlBuf; |
|
31 |
|
32 _LIT(KTestDir, "c:\\test\\"); |
|
33 _LIT(KDbName1, "c:\\test\\t_sqlcompact1_1.db"); |
|
34 _LIT(KDbName2, "c:\\test\\t_sqlcompact1_2.db"); |
|
35 _LIT(KDbName3, "c:\\test\\t_sqlcompact1_3.db"); |
|
36 _LIT(KDbName4, "c:\\test\\t_sqlcompact1_4.db"); |
|
37 |
|
38 _LIT(KAttachName1, "UOOO"); |
|
39 _LIT(KAttachName2, "FOOO"); |
|
40 _LIT(KAttachName3, "AOOO"); |
|
41 _LIT(KAttachName4, "EOOO"); |
|
42 |
|
43 /////////////////////////////////////////////////////////////////////////////////////// |
|
44 |
|
45 void DestroyTestEnv() |
|
46 { |
|
47 TheDb.Close(); |
|
48 (void)RSqlDatabase::Delete(KDbName4); |
|
49 (void)RSqlDatabase::Delete(KDbName3); |
|
50 (void)RSqlDatabase::Delete(KDbName2); |
|
51 (void)RSqlDatabase::Delete(KDbName1); |
|
52 } |
|
53 |
|
54 /////////////////////////////////////////////////////////////////////////////////////// |
|
55 /////////////////////////////////////////////////////////////////////////////////////// |
|
56 //Test macros and functions |
|
57 void Check(TInt aValue, TInt aLine) |
|
58 { |
|
59 if(!aValue) |
|
60 { |
|
61 DestroyTestEnv(); |
|
62 RDebug::Print(_L("*** Test failure. Boolean expression evaluates to false.\r\n")); |
|
63 TheTest(EFalse, aLine); |
|
64 } |
|
65 } |
|
66 void Check2(TInt aValue, TInt aExpected, TInt aLine) |
|
67 { |
|
68 if(aValue != aExpected) |
|
69 { |
|
70 DestroyTestEnv(); |
|
71 RDebug::Print(_L("*** Expected error: %d, got: %d\r\n"), aExpected, aValue); |
|
72 TheTest(EFalse, aLine); |
|
73 } |
|
74 } |
|
75 #define TEST(arg) ::Check((arg), __LINE__) |
|
76 #define TEST2(aValue, aExpected) ::Check2(aValue, aExpected, __LINE__) |
|
77 |
|
78 /////////////////////////////////////////////////////////////////////////////////////// |
|
79 |
|
80 void CreateTestEnv() |
|
81 { |
|
82 RFs fs; |
|
83 TInt err = fs.Connect(); |
|
84 TEST2(err, KErrNone); |
|
85 |
|
86 err = fs.MkDir(KTestDir); |
|
87 TEST(err == KErrNone || err == KErrAlreadyExists); |
|
88 |
|
89 fs.Close(); |
|
90 } |
|
91 |
|
92 /////////////////////////////////////////////////////////////////////////////////////// |
|
93 |
|
94 void ReplaceDb(const TDesC& aDbName, TInt aPageSize) |
|
95 { |
|
96 (void)RSqlDatabase::Delete(aDbName); |
|
97 _LIT8(KConfigStr, "compaction=manual;page_size="); |
|
98 TBuf8<50> config; |
|
99 config.Copy(KConfigStr); |
|
100 config.AppendNum(aPageSize); |
|
101 TInt err = TheDb.Create(aDbName, &config); |
|
102 TEST2(err, KErrNone); |
|
103 TheDb.Close(); |
|
104 } |
|
105 |
|
106 void CreateTable(const TDesC& aDbName) |
|
107 { |
|
108 TInt err = TheDb.Open(aDbName); |
|
109 TEST2(err, KErrNone); |
|
110 err = TheDb.Exec(_L("CREATE TABLE A(I INTEGER, T TEXT)")); |
|
111 TEST(err >= 0); |
|
112 TheDb.Close(); |
|
113 } |
|
114 |
|
115 void InsertRecords(const TDesC& aDbName) |
|
116 { |
|
117 TInt err = TheDb.Open(aDbName); |
|
118 TEST2(err, KErrNone); |
|
119 TheText.SetLength(TheText.MaxLength()); |
|
120 TheText.Fill(TChar('A')); |
|
121 for(TInt i=0;i<100;++i) |
|
122 { |
|
123 TheSqlBuf.Format(_L("INSERT INTO A VALUES(%d, '%S')"), i + 1, &TheText); |
|
124 err = TheDb.Exec(TheSqlBuf); |
|
125 TEST2(err, 1); |
|
126 } |
|
127 TheDb.Close(); |
|
128 } |
|
129 |
|
130 TInt DeleteRecords(const TDesC& aDbName, TInt aPageCount, TInt aPageSize) |
|
131 { |
|
132 TInt freePageCount = -1; |
|
133 TInt err = TheDb.Open(aDbName); |
|
134 TEST2(err, KErrNone); |
|
135 for(TInt i=0;;++i) |
|
136 { |
|
137 TheSqlBuf.Format(_L("DELETE FROM A WHERE I=%d"), i + 1); |
|
138 err = TheDb.Exec(TheSqlBuf); |
|
139 TEST2(err, 1); |
|
140 RSqlDatabase::TSize s; |
|
141 err = TheDb.Size(s); |
|
142 TEST2(err, KErrNone); |
|
143 freePageCount = s.iFree / aPageSize; |
|
144 if(freePageCount >= aPageCount) |
|
145 { |
|
146 break; |
|
147 } |
|
148 } |
|
149 TheDb.Close(); |
|
150 return freePageCount; |
|
151 } |
|
152 |
|
153 /** |
|
154 @SYMTestCaseID SYSLIB-SQL-UT-4072 |
|
155 @SYMTestCaseDesc Manual compaction on attached databases with different page size. |
|
156 The test creates couple of databases with manual compaction and |
|
157 different page sizes, then inserts some records and deletes part of |
|
158 the just inserted records thus making some free pages. |
|
159 The test opens the first database and attaches all other databases the the first one. |
|
160 Then the test checks that RSqlDatabase::Size() returns correct information |
|
161 about the free database space. The test runs the manual compaction on the |
|
162 databases and checks again that the free database space is reported correctly. |
|
163 @SYMTestPriority Medium |
|
164 @SYMTestActions Manual compaction on attached databases with different page size. |
|
165 @SYMTestExpectedResults Test must not fail |
|
166 @SYMREQ REQ10405 |
|
167 REQ10407 |
|
168 */ |
|
169 void CompactDbTest1() |
|
170 { |
|
171 const TPtrC KDbName[] = {KDbName1(), KDbName2(), KDbName3(), KDbName4()}; |
|
172 const TPtrC KDbAttachName[]={KAttachName1(),KAttachName2(), KAttachName3(), KAttachName4()}; |
|
173 const TInt KDbPageSize[] = {8192, 1024, 4096, 2048}; |
|
174 const TInt KFreePageCount[]={9, 30, 17, 7}; |
|
175 TInt freePageCount[] ={0, 0, 0, 0}; |
|
176 const TInt KSize = sizeof(KDbName) / sizeof(KDbName[0]); |
|
177 |
|
178 TInt i; |
|
179 |
|
180 //Create databases, tables, insert records, delete part of the just inserted records. |
|
181 for(i=0;i<KSize;++i) |
|
182 { |
|
183 ReplaceDb(KDbName[i], KDbPageSize[i]); |
|
184 CreateTable(KDbName[i]); |
|
185 InsertRecords(KDbName[i]); |
|
186 freePageCount[i] = DeleteRecords(KDbName[i], KFreePageCount[i], KDbPageSize[i]); |
|
187 } |
|
188 |
|
189 //Open the first database, attach all others. |
|
190 TInt err = TheDb.Open(KDbName1()); |
|
191 TEST2(err, KErrNone); |
|
192 for(i=0;i<KSize;++i) |
|
193 { |
|
194 err = TheDb.Attach(KDbName[i], KDbAttachName[i]); |
|
195 TEST2(err, KErrNone); |
|
196 } |
|
197 |
|
198 //Check the size of the main database. |
|
199 RSqlDatabase::TSize size; |
|
200 err = TheDb.Size(size); |
|
201 TEST2(err, KErrNone); |
|
202 TEST2((size.iFree / KDbPageSize[0]), freePageCount[0]); |
|
203 |
|
204 //For all attached database: check the size of the database, compact, check the size again. |
|
205 for(i=0;i<KSize;++i) |
|
206 { |
|
207 err = TheDb.Size(size, KDbAttachName[i]); |
|
208 TEST2(err, KErrNone); |
|
209 TEST2((size.iFree / KDbPageSize[i]), freePageCount[i]); |
|
210 |
|
211 const TInt KCompactPageCount = 3; |
|
212 TInt rc = TheDb.Compact(KCompactPageCount * KDbPageSize[i], KDbAttachName[i]); |
|
213 TInt expected = KCompactPageCount * KDbPageSize[i]; |
|
214 TEST2(rc, expected); |
|
215 err = TheDb.Size(size, KDbAttachName[i]); |
|
216 TEST2(err, KErrNone); |
|
217 TInt count = size.iFree / KDbPageSize[i]; |
|
218 expected = freePageCount[i] - KCompactPageCount; |
|
219 TEST2(count, expected); |
|
220 } |
|
221 |
|
222 //Detach databases and close the main database. |
|
223 for(i=0;i<KSize;++i) |
|
224 { |
|
225 err = TheDb.Detach(KDbAttachName[i]); |
|
226 TEST2(err, KErrNone); |
|
227 } |
|
228 TheDb.Close(); |
|
229 |
|
230 //Cleanup. |
|
231 for(i=0;i<KSize;++i) |
|
232 { |
|
233 (void)RSqlDatabase::Delete(KDbName[i]); |
|
234 } |
|
235 } |
|
236 |
|
237 //Creates a test database (with KDbName1 name). Inserts aRecordCount records. |
|
238 //The page size is specified in aPageSize parameter. But practically the page size is always 1024 bytes. |
|
239 //The record size is such that there is only one record per page. |
|
240 void PrepareDb(TInt aPageSize, TInt aRecordCount, TBool aManualCompaction = EFalse) |
|
241 { |
|
242 //Create the database |
|
243 (void)RSqlDatabase::Delete(KDbName1); |
|
244 _LIT8(KConfigStr, "page_size="); |
|
245 TBuf8<100> config; |
|
246 config.Copy(KConfigStr); |
|
247 config.AppendNum(aPageSize); |
|
248 if(aManualCompaction) |
|
249 { |
|
250 config.Append(_L(";compaction=manual;")); |
|
251 } |
|
252 TInt err = TheDb.Create(KDbName1, &config); |
|
253 TEST2(err, KErrNone); |
|
254 |
|
255 err = TheDb.Exec(_L("CREATE TABLE A(I INTEGER, T TEXT)")); |
|
256 TEST(err >= 0); |
|
257 //Insert records |
|
258 TheText.SetLength(TheText.MaxLength()); |
|
259 TheText.Fill(TChar('A')); |
|
260 for(TInt i=0;i<aRecordCount;++i) |
|
261 { |
|
262 TheSqlBuf.Format(_L("INSERT INTO A VALUES(%d, '%S')"), i + 1, &TheText); |
|
263 err = TheDb.Exec(TheSqlBuf); |
|
264 TEST2(err, 1); |
|
265 } |
|
266 //Delete all records making a lot of free pages. This operation should kick-off the background compaction |
|
267 err = TheDb.Exec(_L("DELETE FROM A WHERE 1")); |
|
268 TEST2(err, aRecordCount); |
|
269 } |
|
270 |
|
271 /** |
|
272 @SYMTestCaseID SYSLIB-SQL-UT-4073 |
|
273 @SYMTestCaseDesc Background compaction steps test. |
|
274 The test creates a database with background compaction mode, |
|
275 then inserts records and deletes all of them. The count of records is such that when |
|
276 the records get deleted, the number of the free pages is very big and all free pages cannot |
|
277 be removed for just one compaction step. |
|
278 The test waits for ("compaction interval"/10 ms) time and checks that no compaction |
|
279 step has been run by the server during the pause and the free space size is the same as before the |
|
280 pause. Then the test waits for ("compaction interval" + "compaction step") time and checks that |
|
281 the background compaction step really happened and removed only part of the free pages. |
|
282 The same test is repeated again and the same check is performed again. |
|
283 @SYMTestPriority Medium |
|
284 @SYMTestActions Background compaction steps test. |
|
285 @SYMTestExpectedResults Test must not fail |
|
286 @SYMREQ REQ10271 |
|
287 REQ10272 |
|
288 */ |
|
289 void CompactDbTest2() |
|
290 { |
|
291 const TInt KPageSize = 1024; |
|
292 //Number of records to be added and removed from database. Need to be increased when testing on a faster |
|
293 // hardware, otherwise at fastest case the background compaction could be finished in just 1 step. |
|
294 const TInt KRecordCount = 2000; |
|
295 PrepareDb(KPageSize, KRecordCount); |
|
296 |
|
297 //Check the free space-1 |
|
298 RSqlDatabase::TSize size1; |
|
299 TInt err = TheDb.Size(size1); |
|
300 TEST2(err, KErrNone); |
|
301 TheTest.Printf(_L("===Free space before compaction, pages=%d\r\n"), size1.iFree / KPageSize); |
|
302 TEST(size1.iSize >= (KRecordCount * KPageSize)); |
|
303 |
|
304 //Wait KSqlCompactStepIntervalMs/10 ms. The background compaction should not be kicked-off. |
|
305 TTime time1; |
|
306 time1.HomeTime(); |
|
307 User::After((KSqlCompactStepIntervalMs / 10) * 1000); |
|
308 TTime time2; |
|
309 time2.HomeTime(); |
|
310 TTimeIntervalMicroSeconds intervalUs = time2.MicroSecondsFrom(time1); |
|
311 //Check the free space-2 |
|
312 RSqlDatabase::TSize size2; |
|
313 err = TheDb.Size(size2); |
|
314 TEST2(err, KErrNone); |
|
315 TheTest.Printf(_L("=== Wait time: %ld ms. Free space after compaction-1, pages=%d\r\n"), intervalUs.Int64() / 1000 ,size2.iFree / KPageSize); |
|
316 if(intervalUs > KSqlCompactStepIntervalMs * 1000) |
|
317 { |
|
318 TEST(size2.iFree <= size1.iFree); |
|
319 } |
|
320 else |
|
321 { |
|
322 TEST(size2.iFree == size1.iFree); |
|
323 } |
|
324 |
|
325 //Wait (KSqlCompactStepIntervalMs + KSqlCompactStepLengthMs) ms. During the pause only part of the free pages |
|
326 //should be removed (whatever can be completed for KSqlCompactStepLengthMs ms). |
|
327 User::After((KSqlCompactStepIntervalMs + KSqlCompactStepLengthMs) * 1000); |
|
328 //Check the free space-3 |
|
329 RSqlDatabase::TSize size3; |
|
330 err = TheDb.Size(size3); |
|
331 TEST2(err, KErrNone); |
|
332 TheTest.Printf(_L("===Free space after compaction-2, pages=%d\r\n"), size3.iFree / KPageSize); |
|
333 if(size3.iFree == 0) |
|
334 { |
|
335 TheTest.Printf(_L("WARNING: Background compaction finished in 1 step. Initial number of records need to be increased.\r\n")); |
|
336 } |
|
337 TEST(size3.iFree > 0 && size3.iFree < size2.iFree); |
|
338 |
|
339 //Wait another (KSqlCompactStepIntervalMs + KSqlCompactStepLengthMs) ms. During the pause only part of the free pages |
|
340 //should be removed (whatever can be completed for KSqlCompactStepLengthMs ms). |
|
341 User::After((KSqlCompactStepIntervalMs + KSqlCompactStepLengthMs) * 1000); |
|
342 //Check the free space-4 |
|
343 RSqlDatabase::TSize size4; |
|
344 err = TheDb.Size(size4); |
|
345 TEST2(err, KErrNone); |
|
346 TheTest.Printf(_L("===Free space after compaction-3, pages=%d\r\n"), size4.iFree / KPageSize); |
|
347 TEST((size4.iFree > 0 && size4.iFree < size3.iFree) || (size4.iFree == 0)); |
|
348 |
|
349 //Cleanup |
|
350 TheDb.Close(); |
|
351 (void)RSqlDatabase::Delete(KDbName1); |
|
352 } |
|
353 |
|
354 /** |
|
355 @SYMTestCaseID SYSLIB-SQL-UT-4074 |
|
356 @SYMTestCaseDesc Background compaction timer test. |
|
357 The test creates a database with background compaction mode, |
|
358 then inserts records and deletes all of them. The count of records is such that when |
|
359 the records get deleted, the number of the free pages is very big and all free pages cannot |
|
360 be removed for just one compaction step. |
|
361 Then the test executes a set of operations with the server. The amount of time needed for the |
|
362 operations to be executed is bigger than the ("compaction interval" + "compaction step") time. |
|
363 No compaction step should be executed during that time, because every operation resets the background |
|
364 compaction timer. |
|
365 @SYMTestPriority Medium |
|
366 @SYMTestActions Background compaction timer test. |
|
367 @SYMTestExpectedResults Test must not fail |
|
368 @SYMREQ REQ10271 |
|
369 REQ10272 |
|
370 */ |
|
371 void CompactDbTest3() |
|
372 { |
|
373 const TInt KPageSize = 1024; |
|
374 const TInt KRecordCount = 1000; |
|
375 PrepareDb(KPageSize, KRecordCount); |
|
376 |
|
377 //Check the free space-1 |
|
378 RSqlDatabase::TSize size1; |
|
379 TInt err = TheDb.Size(size1); |
|
380 TEST2(err, KErrNone); |
|
381 TheTest.Printf(_L("===Free space before operations, pages=%d. Db.Size=%d, Db.Free=%d\r\n"), size1.iFree / KPageSize, size1.iSize, size1.iFree); |
|
382 TEST(size1.iSize >= (KRecordCount * KPageSize)); |
|
383 |
|
384 //Execute a set of operations. The time needed for the operations to complete is bigger than |
|
385 //(KSqlCompactStepIntervalMs + KSqlCompactStepLengthMs) ms |
|
386 TInt freq = 0; |
|
387 TEST2(HAL::Get(HAL::EFastCounterFrequency, freq), KErrNone); |
|
388 TUint32 begin = User::FastCounter(); |
|
389 TInt count = 0; |
|
390 TInt time = -1; |
|
391 for(;;++count) |
|
392 { |
|
393 err = TheDb.Exec(_L("SELECT COUNT(*) FROM A")); |
|
394 TEST(err >= 0); |
|
395 TUint32 current = User::FastCounter(); |
|
396 TInt64 diffTicks = (TInt64)current - (TInt64)begin; |
|
397 if(diffTicks < 0) |
|
398 { |
|
399 diffTicks = KMaxTUint32 + diffTicks + 1; |
|
400 } |
|
401 const TInt KMicroSecIn1Sec = 1000000; |
|
402 TInt32 us = (diffTicks * KMicroSecIn1Sec) / freq; |
|
403 time = us / 1000; |
|
404 if(time > ((KSqlCompactStepIntervalMs + KSqlCompactStepLengthMs))) |
|
405 { |
|
406 break; |
|
407 } |
|
408 } |
|
409 //Check the free space-2 |
|
410 RSqlDatabase::TSize size2; |
|
411 err = TheDb.Size(size2); |
|
412 TEST2(err, KErrNone); |
|
413 TheTest.Printf(_L("===%d operations completed for %d ms\r\n"), count, time); |
|
414 TheTest.Printf(_L("===Free space after operations, pages=%d\r\n"), size2.iFree / KPageSize); |
|
415 TEST(size1.iFree == size2.iFree); |
|
416 |
|
417 //Cleanup |
|
418 TheDb.Close(); |
|
419 (void)RSqlDatabase::Delete(KDbName1); |
|
420 } |
|
421 |
|
422 /** |
|
423 @SYMTestCaseID SYSLIB-SQL-UT-4103 |
|
424 @SYMTestCaseDesc Big manual compaction test. |
|
425 The test creates a database with 1000 free pages, then calls |
|
426 RSqlDatabase::Compact(RSqlDatabase::EMaxCompaction). |
|
427 @SYMTestPriority Medium |
|
428 @SYMTestActions Big manual compaction test. |
|
429 @SYMTestExpectedResults Test must not fail |
|
430 @SYMREQ REQ10271 |
|
431 REQ10272 |
|
432 */ |
|
433 void ManualCompactTest() |
|
434 { |
|
435 //Create a database with 1000 free pages |
|
436 const TInt KPageSize = 1024; |
|
437 const TInt KRecordCount = 1000; |
|
438 PrepareDb(KPageSize, KRecordCount, ETrue);//create the database with manual compaction mode |
|
439 //Check the free space-1 |
|
440 RSqlDatabase::TSize size1; |
|
441 TInt err = TheDb.Size(size1); |
|
442 TEST2(err, KErrNone); |
|
443 const TInt KFreePageCount = size1.iFree / KPageSize; |
|
444 TheTest.Printf(_L("===Free space before operations, pages=%d\r\n"), KFreePageCount); |
|
445 TEST(size1.iSize >= (KRecordCount * KPageSize)); |
|
446 //Compact |
|
447 err = TheDb.Compact(RSqlDatabase::EMaxCompaction); |
|
448 TEST2(err, size1.iFree); |
|
449 //Cleanup |
|
450 TheDb.Close(); |
|
451 (void)RSqlDatabase::Delete(KDbName1); |
|
452 } |
|
453 |
|
454 void DoTestsL() |
|
455 { |
|
456 TheTest.Start(_L(" @SYMTestCaseID:SYSLIB-SQL-UT-4072 Manual Compact() - attached databases, different page sizes")); |
|
457 CompactDbTest1(); |
|
458 |
|
459 TheTest.Next( _L(" @SYMTestCaseID:SYSLIB-SQL-UT-4073 Background compaction steps test")); |
|
460 CompactDbTest2(); |
|
461 |
|
462 TheTest.Next( _L(" @SYMTestCaseID:SYSLIB-SQL-UT-4074 Background compaction timer test")); |
|
463 CompactDbTest3(); |
|
464 |
|
465 TheTest.Next( _L(" @SYMTestCaseID:SYSLIB-SQL-UT-4103 Big manual compaction test")); |
|
466 ManualCompactTest(); |
|
467 } |
|
468 |
|
469 TInt E32Main() |
|
470 { |
|
471 TheTest.Title(); |
|
472 |
|
473 CTrapCleanup* tc = CTrapCleanup::New(); |
|
474 TheTest(tc != NULL); |
|
475 |
|
476 __UHEAP_MARK; |
|
477 |
|
478 CreateTestEnv(); |
|
479 TRAPD(err, DoTestsL()); |
|
480 DestroyTestEnv(); |
|
481 TEST2(err, KErrNone); |
|
482 |
|
483 __UHEAP_MARKEND; |
|
484 |
|
485 TheTest.End(); |
|
486 TheTest.Close(); |
|
487 |
|
488 delete tc; |
|
489 |
|
490 User::Heap().Check(); |
|
491 return KErrNone; |
|
492 } |