|
1 // Copyright (c) 2005-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 // Testing DBMS ordering and searching functionality when the key field is unicode string. |
|
15 // The idea is to verify that collation level 0 is used when doing string based searching |
|
16 // and collation level > 0 is used when doing string based ordering. |
|
17 // |
|
18 // |
|
19 |
|
20 #include <e32test.h> |
|
21 #include <f32file.h> |
|
22 #include <d32dbms.h> |
|
23 |
|
24 ///////////////////////////////////////////////////////////////// |
|
25 //Globals |
|
26 |
|
27 _LIT( KTestDatabase, "C:\\DBMS-TST\\T_DbmsStrComp.DB"); |
|
28 |
|
29 static RTest TheTest(_L("t_dbstrcmp")); |
|
30 static RFs TheFs; |
|
31 static RDbNamedDatabase TheDb; |
|
32 static RDbs TheDbSession; |
|
33 |
|
34 //Test table defs |
|
35 _LIT(KTestTableName1, "TABLE1");//EDbColText16 key field |
|
36 _LIT(KTestTableName2, "TABLE2");//EDbColLongText16 key field |
|
37 |
|
38 struct TColDef |
|
39 { |
|
40 const TText* iName; |
|
41 TDbColType iType; |
|
42 TInt iAttributes; |
|
43 }; |
|
44 static TColDef const KColDefs1[]= |
|
45 { |
|
46 {_S("ID"), EDbColText16, 0}, |
|
47 {_S("DATA"), EDbColUint32, 0}, |
|
48 {0} |
|
49 }; |
|
50 static TColDef const KColDefs2[]= |
|
51 { |
|
52 {_S("ID"), EDbColLongText16, 0}, |
|
53 {_S("DATA"), EDbColUint32, 0}, |
|
54 {0} |
|
55 }; |
|
56 |
|
57 //Test strings |
|
58 const TInt KTestStrLen = 3; //The length of test strings |
|
59 typedef TBuf16<KTestStrLen> TNameBuf; |
|
60 //Test strings array - using upper and lower case - which will force the DBMS server to make |
|
61 //different decisions depending on what is the current case: ordering or searching. |
|
62 const TNameBuf KTestStr[] = |
|
63 { |
|
64 _L16("aaa"), |
|
65 _L16("aAa"), |
|
66 _L16("bbB"), |
|
67 _L16("BbB") |
|
68 }; |
|
69 const TInt KTestStrCnt = sizeof(KTestStr) / sizeof(KTestStr[0]); |
|
70 |
|
71 /////////////////////////////////////////////////////////////////////////////////////// |
|
72 /////////////////////////////////////////////////////////////////////////////////////// |
|
73 //Destroy test environment - global functions |
|
74 |
|
75 //Deletes "aFullName" file. |
|
76 static TInt DeleteDataFile(const TDesC& aFullName) |
|
77 { |
|
78 RFs fsSession; |
|
79 TInt err = fsSession.Connect(); |
|
80 if(err == KErrNone) |
|
81 { |
|
82 TEntry entry; |
|
83 err = fsSession.Entry(aFullName, entry); |
|
84 if(err == KErrNone) |
|
85 { |
|
86 RDebug::Print(_L("Deleting \"%S\" file.\n"), &aFullName); |
|
87 err = fsSession.SetAtt(aFullName, 0, KEntryAttReadOnly); |
|
88 if(err != KErrNone) |
|
89 { |
|
90 RDebug::Print(_L("Error %d changing \"%S\" file attributes.\n"), err, &aFullName); |
|
91 } |
|
92 err = fsSession.Delete(aFullName); |
|
93 if(err != KErrNone) |
|
94 { |
|
95 RDebug::Print(_L("Error %d deleting \"%S\" file.\n"), err, &aFullName); |
|
96 } |
|
97 } |
|
98 fsSession.Close(); |
|
99 } |
|
100 else |
|
101 { |
|
102 RDebug::Print(_L("Error %d connecting file session. File: %S.\n"), err, &aFullName); |
|
103 } |
|
104 return err; |
|
105 } |
|
106 |
|
107 /////////////////////////////////////////////////////////////////////////////////////// |
|
108 /////////////////////////////////////////////////////////////////////////////////////// |
|
109 //Tests macros and functions. |
|
110 //If (!aValue) then the test will be panicked, the test data files will be deleted. |
|
111 static void Check(TInt aValue, TInt aLine) |
|
112 { |
|
113 if(!aValue) |
|
114 { |
|
115 ::DeleteDataFile(KTestDatabase); |
|
116 TheTest(EFalse, aLine); |
|
117 } |
|
118 } |
|
119 //If (aValue != aExpected) then the test will be panicked, the test data files will be deleted. |
|
120 static void Check(TInt aValue, TInt aExpected, TInt aLine) |
|
121 { |
|
122 if(aValue != aExpected) |
|
123 { |
|
124 RDebug::Print(_L("*** Expected error: %d, got: %d\r\n"), aExpected, aValue); |
|
125 ::DeleteDataFile(KTestDatabase); |
|
126 TheTest(EFalse, aLine); |
|
127 } |
|
128 } |
|
129 //Use these to test conditions. |
|
130 #define TEST(arg) ::Check((arg), __LINE__) |
|
131 #define TEST2(aValue, aExpected) ::Check(aValue, aExpected, __LINE__) |
|
132 |
|
133 /////////////////////////////////////////////////////////////////////////////////////// |
|
134 /////////////////////////////////////////////////////////////////////////////////////// |
|
135 //Global functions |
|
136 |
|
137 //Prepares the test directory. |
|
138 //TheFs.Connect() has to be called already. |
|
139 static void SetupTestDirectory() |
|
140 { |
|
141 TInt err = TheFs.MkDir(KTestDatabase); |
|
142 TEST(err == KErrNone || err == KErrAlreadyExists); |
|
143 } |
|
144 |
|
145 //Leaves with info message printed out |
|
146 static void LeaveL(TInt aError, TInt aLine) |
|
147 { |
|
148 RDebug::Print(_L("*** Leave. Error: %d, Line: %d\r\n"), aError, aLine); |
|
149 User::Leave(aError); |
|
150 } |
|
151 |
|
152 //Leaves if aError < 0 with info message printed out |
|
153 static void LeaveIfErrorL(TInt aError, TInt aLine) |
|
154 { |
|
155 if(aError < KErrNone) |
|
156 { |
|
157 LeaveL(aError, aLine); |
|
158 } |
|
159 } |
|
160 |
|
161 //Use LEAVE() macro instead of User::Leave() and LEAVE_IF_ERROR() macro instead of |
|
162 //User::LeaveIfError(). They will print the line number, where the "leave" was called. |
|
163 #define LEAVE(aError) ::LeaveL(aError, __LINE__) |
|
164 #define LEAVE_IF_ERROR(aError) ::LeaveIfErrorL(aError, __LINE__) |
|
165 |
|
166 //Creates the test DBMS session |
|
167 static void CreateTestDbSession() |
|
168 { |
|
169 RDebug::Print(_L("Create DBMS session\n")); |
|
170 TInt err = TheDbSession.Connect(); |
|
171 TEST2(err, KErrNone); |
|
172 } |
|
173 |
|
174 |
|
175 //Creates the test database |
|
176 //TheDbSession instance has to be connected already. |
|
177 //TheFs.Connect() has to be called already. |
|
178 static void CreateTestDatabase(RDbs& aDbs, RDbNamedDatabase& aDb) |
|
179 { |
|
180 RDebug::Print(_L("Create test database\n")); |
|
181 TInt err = aDb.Replace(TheFs, KTestDatabase); |
|
182 TEST2(err, KErrNone); |
|
183 TheDb.Close(); |
|
184 err = aDb.Open(aDbs, KTestDatabase); |
|
185 TEST2(err, KErrNone); |
|
186 } |
|
187 |
|
188 //Creates test table |
|
189 static void DoCreateTestTableL(RDbNamedDatabase& aDb, const TDesC& aTblName, const TColDef aColDefs[]) |
|
190 { |
|
191 CDbColSet* colSet = CDbColSet::NewLC(); |
|
192 for(const TColDef* colDef=aColDefs;colDef->iName;++colDef) |
|
193 { |
|
194 TDbCol col(TPtrC(colDef->iName), colDef->iType); |
|
195 col.iAttributes = colDef->iAttributes; |
|
196 colSet->AddL(col); |
|
197 } |
|
198 TEST2(aDb.CreateTable(aTblName, *colSet), KErrNone); |
|
199 CleanupStack::PopAndDestroy(colSet); |
|
200 } |
|
201 |
|
202 //Creates test tables |
|
203 static void CreateTestTablesL(RDbNamedDatabase& aDb) |
|
204 { |
|
205 RDebug::Print(_L("Create test tables\n")); |
|
206 ::DoCreateTestTableL(aDb, KTestTableName1, KColDefs1); |
|
207 ::DoCreateTestTableL(aDb, KTestTableName2, KColDefs2); |
|
208 } |
|
209 |
|
210 //Gets the value of the string field, which type may be EDbColText16 or EDbColLongText16 |
|
211 void GetStrFieldValueL(RDbRowSet& aTbl, const TDesC& aTblName, TDes& aStrFldVal) |
|
212 { |
|
213 if(aTblName.CompareF(KTestTableName1) == 0) |
|
214 { |
|
215 aStrFldVal = aTbl.ColDes16(1); |
|
216 } |
|
217 else |
|
218 { |
|
219 RDbColReadStream blob; |
|
220 blob.OpenLC(aTbl, 1); |
|
221 blob.ReadL(aStrFldVal, aTbl.ColLength(1)); |
|
222 CleanupStack::PopAndDestroy(); |
|
223 } |
|
224 } |
|
225 |
|
226 //Prints all table records |
|
227 static TInt PrintRecordsL(RDbRowSet& aTbl, const TDesC& aTblName) |
|
228 { |
|
229 RDebug::Print(_L("Table: %S\n"), &aTblName); |
|
230 aTbl.FirstL(); |
|
231 TInt rec = 0; |
|
232 while(aTbl.AtRow()) |
|
233 { |
|
234 aTbl.GetL(); |
|
235 TNameBuf strFldVal; |
|
236 GetStrFieldValueL(aTbl, aTblName, strFldVal); |
|
237 TUint32 v = aTbl.ColUint32(2); |
|
238 RDebug::Print(_L(" Record %d, Str: %S, Val: %d\n"), ++rec, &strFldVal, v); |
|
239 aTbl.NextL(); |
|
240 } |
|
241 return rec; |
|
242 } |
|
243 |
|
244 //Checks if the records order (based on a string key field comparison) matches the order of the |
|
245 //strings in aTestStrArray |
|
246 static void AssertRecordsOrderL(RDbRowSet& aTbl, const TDesC& aTblName, const RArray<TNameBuf>& aTestStrArray) |
|
247 { |
|
248 aTbl.FirstL(); |
|
249 TInt rec = 0; |
|
250 while(aTbl.AtRow()) |
|
251 { |
|
252 aTbl.GetL(); |
|
253 TNameBuf strFldVal; |
|
254 GetStrFieldValueL(aTbl, aTblName, strFldVal); |
|
255 TEST(aTestStrArray[rec] == strFldVal); |
|
256 ++rec; |
|
257 aTbl.NextL(); |
|
258 } |
|
259 } |
|
260 |
|
261 //Adds test data to the specified table. Make sure that the records are not in |
|
262 //order (assuming that the first field will be the key). |
|
263 static void AddTestDataL(RDbNamedDatabase& aDb, const TDesC& aTblName) |
|
264 { |
|
265 RDbTable tbl; |
|
266 CleanupClosePushL(tbl); |
|
267 TEST2(tbl.Open(aDb, aTblName, RDbRowSet::EUpdatable), KErrNone); |
|
268 for(TInt i=0;i<KTestStrCnt;++i) |
|
269 { |
|
270 tbl.InsertL(); |
|
271 tbl.SetColL(1, KTestStr[KTestStrCnt - i - 1]); |
|
272 tbl.SetColL(2, i + 1); |
|
273 tbl.PutL(); |
|
274 } |
|
275 TEST(tbl.CountL() == KTestStrCnt); |
|
276 (void)::PrintRecordsL(tbl, aTblName); |
|
277 CleanupStack::PopAndDestroy(&tbl); |
|
278 } |
|
279 |
|
280 //Adds the test data to test tables |
|
281 static void AddTestDataL(RDbNamedDatabase& aDb) |
|
282 { |
|
283 RDebug::Print(_L("Add data to test tables\n")); |
|
284 ::AddTestDataL(aDb, KTestTableName1); |
|
285 ::AddTestDataL(aDb, KTestTableName2); |
|
286 } |
|
287 |
|
288 //Init test environment |
|
289 static void InitEnvL() |
|
290 { |
|
291 ::CreateTestDbSession(); |
|
292 //Create test database and tables. Add some test data to them. |
|
293 ::CreateTestDatabase(TheDbSession, TheDb); |
|
294 ::CreateTestTablesL(TheDb); |
|
295 ::AddTestDataL(TheDb); |
|
296 } |
|
297 |
|
298 //String comparison function, used in FillStrArraySorted() function. |
|
299 static TInt CompareC(const TNameBuf& aName1, const TNameBuf& aName2) |
|
300 { |
|
301 return aName1.CompareC(aName2); |
|
302 } |
|
303 |
|
304 //Inserts all test string into an ordered array - aTestStrArray |
|
305 static void FillStrArraySortedL(RArray<TNameBuf>& aTestStrArray) |
|
306 { |
|
307 for(TInt i=0;i<KTestStrCnt;++i) |
|
308 { |
|
309 User::LeaveIfError(aTestStrArray.InsertInOrder(KTestStr[i], TLinearOrder<TNameBuf>(CompareC))); |
|
310 } |
|
311 } |
|
312 |
|
313 static void CreateIndexL(RDbNamedDatabase& aDb, const TDesC& aTblName, const TDesC& aColumnName) |
|
314 { |
|
315 RDebug::Print(_L("Create index. Table: %S, column: %S\n"), &aTblName, &aColumnName); |
|
316 CDbKey* key = CDbKey::NewLC(); |
|
317 key->AddL(aColumnName); |
|
318 key->MakeUnique(); |
|
319 key->SetComparison(EDbCompareCollated); |
|
320 LEAVE_IF_ERROR(aDb.CreateIndex(aColumnName, aTblName, *key)); |
|
321 CleanupStack::PopAndDestroy(key); |
|
322 } |
|
323 |
|
324 /////////////////////////////////////////////////////////////////////////////////////// |
|
325 /////////////////////////////////////////////////////////////////////////////////////// |
|
326 //Test cases |
|
327 |
|
328 //Test case 1. Check SELECT statement with ORDER BY clause when the key field is a string. |
|
329 static void OrderByTestL(RDbNamedDatabase& aDb, const TDesC& aTblName, const RArray<TNameBuf>& aTestStrArray) |
|
330 { |
|
331 RDbView view; |
|
332 CleanupClosePushL(view); |
|
333 |
|
334 TBuf<128> sqlStmt; |
|
335 sqlStmt.Append(_L("SELECT ID, DATA FROM ")); |
|
336 sqlStmt.Append(aTblName); |
|
337 sqlStmt.Append(_L(" ORDER BY ID")); |
|
338 User::LeaveIfError(view.Prepare(aDb, TDbQuery(sqlStmt, EDbCompareCollated), TDbWindow::EUnlimited)); |
|
339 User::LeaveIfError(view.EvaluateAll()); |
|
340 |
|
341 (void)::PrintRecordsL(view, aTblName); |
|
342 AssertRecordsOrderL(view, aTblName, aTestStrArray); |
|
343 |
|
344 CleanupStack::PopAndDestroy(&view); |
|
345 } |
|
346 |
|
347 //Test case 2. Check SELECT statement with LIKE keyword when the key field is a string. |
|
348 static void LikeTestL(RDbNamedDatabase& aDb, const TDesC& aTblName) |
|
349 { |
|
350 RDbView view; |
|
351 CleanupClosePushL(view); |
|
352 |
|
353 TBuf<128> sqlStmt; |
|
354 sqlStmt.Append(_L("SELECT ID, DATA FROM ")); |
|
355 sqlStmt.Append(aTblName); |
|
356 sqlStmt.Append(_L(" WHERE ID LIKE 'B*'")); |
|
357 User::LeaveIfError(view.Prepare(aDb, TDbQuery(sqlStmt, EDbCompareCollated), TDbWindow::EUnlimited)); |
|
358 User::LeaveIfError(view.EvaluateAll()); |
|
359 |
|
360 TInt cnt = ::PrintRecordsL(view, aTblName); |
|
361 TEST(cnt == 2); |
|
362 |
|
363 CleanupStack::PopAndDestroy(&view); |
|
364 } |
|
365 |
|
366 //Test case 3. Check SELECT statement with LIKE & ORDER BY keywords when the key field is a string. |
|
367 static void LikeOrderTestL(RDbNamedDatabase& aDb, const TDesC& aTblName, const RArray<TNameBuf>& aTestStrArray) |
|
368 { |
|
369 RDbView view; |
|
370 CleanupClosePushL(view); |
|
371 |
|
372 TBuf<128> sqlStmt; |
|
373 sqlStmt.Append(_L("SELECT ID, DATA FROM ")); |
|
374 sqlStmt.Append(aTblName); |
|
375 sqlStmt.Append(_L(" WHERE ID LIKE 'B*' ORDER BY ID")); |
|
376 User::LeaveIfError(view.Prepare(aDb, TDbQuery(sqlStmt, EDbCompareCollated), TDbWindow::EUnlimited)); |
|
377 User::LeaveIfError(view.EvaluateAll()); |
|
378 |
|
379 TInt cnt = ::PrintRecordsL(view, aTblName); |
|
380 TEST(cnt == 2); |
|
381 AssertRecordsOrderL(view, aTblName, aTestStrArray); |
|
382 |
|
383 CleanupStack::PopAndDestroy(&view); |
|
384 } |
|
385 |
|
386 //Test case 4. Indexed table. The index is a string field. |
|
387 static void IndexTestL(RDbNamedDatabase& aDb, const TDesC& aTblName, const RArray<TNameBuf>& aTestStrArray) |
|
388 { |
|
389 _LIT(KIdxName, "ID"); |
|
390 ::CreateIndexL(aDb, aTblName, KIdxName); |
|
391 |
|
392 RDbTable tbl; |
|
393 CleanupClosePushL(tbl); |
|
394 TEST2(tbl.Open(aDb, aTblName, RDbRowSet::EReadOnly), KErrNone); |
|
395 TEST2(tbl.SetIndex(KIdxName), KErrNone); |
|
396 |
|
397 (void)::PrintRecordsL(tbl, aTblName); |
|
398 AssertRecordsOrderL(tbl, aTblName, aTestStrArray); |
|
399 |
|
400 CleanupStack::PopAndDestroy(&tbl); |
|
401 } |
|
402 |
|
403 /////////////////////////////////////////////////////////////////////////////////////// |
|
404 /////////////////////////////////////////////////////////////////////////////////////// |
|
405 //The main test function. |
|
406 //Call your new test functions from here |
|
407 static void RunTestsL() |
|
408 { |
|
409 TheTest.Start(_L(" @SYMTestCaseID:SYSLIB-DBMS-LEGACY-DBMSSTRCOMP-0001 Init test environment ")); |
|
410 ::InitEnvL(); |
|
411 |
|
412 RArray<TNameBuf> testStrArray; |
|
413 CleanupClosePushL(testStrArray); |
|
414 ::FillStrArraySortedL(testStrArray); |
|
415 |
|
416 TheTest.Next(_L("SELECT, ORDER BY, EDbColText16")); |
|
417 ::OrderByTestL(TheDb, KTestTableName1, testStrArray); |
|
418 |
|
419 TheTest.Next(_L("SELECT, ORDER BY, EDbColLongText16")); |
|
420 ::OrderByTestL(TheDb, KTestTableName2, testStrArray); |
|
421 |
|
422 TheTest.Next(_L("SELECT, LIKE, EDbColText16")); |
|
423 ::LikeTestL(TheDb, KTestTableName1); |
|
424 |
|
425 TheTest.Next(_L("SELECT, LIKE, EDbColLongText16")); |
|
426 ::LikeTestL(TheDb, KTestTableName2); |
|
427 |
|
428 RArray<TNameBuf> testStrArray2; |
|
429 CleanupClosePushL(testStrArray2); |
|
430 testStrArray2.AppendL(testStrArray[2]);//"bbB" |
|
431 testStrArray2.AppendL(testStrArray[3]);//"BbB" |
|
432 |
|
433 TheTest.Next(_L("SELECT, LIKE, ORDER BY, EDbColText16")); |
|
434 ::LikeOrderTestL(TheDb, KTestTableName1, testStrArray2); |
|
435 |
|
436 TheTest.Next(_L("SELECT, LIKE, ORDER BY, EDbColLongText16")); |
|
437 ::LikeOrderTestL(TheDb, KTestTableName2, testStrArray2); |
|
438 |
|
439 TheTest.Next(_L("Index, EDbColText16")); |
|
440 ::IndexTestL(TheDb, KTestTableName1, testStrArray); |
|
441 |
|
442 // Not possible to create a key with EDbColLongText16 |
|
443 // TheTest.Next(_L("Index, EDbColLongText16")); |
|
444 // ::IndexTestL(TheDb, KTestTableName2, testStrArray); |
|
445 |
|
446 //Add tests here! |
|
447 |
|
448 CleanupStack::PopAndDestroy(&testStrArray2); |
|
449 CleanupStack::PopAndDestroy(&testStrArray); |
|
450 } |
|
451 |
|
452 TInt E32Main() |
|
453 { |
|
454 TheTest.Title(); |
|
455 |
|
456 __UHEAP_MARK; |
|
457 |
|
458 CTrapCleanup* trapCleanup = CTrapCleanup::New(); |
|
459 TEST(trapCleanup != NULL); |
|
460 |
|
461 TInt err = TheFs.Connect(); |
|
462 TEST2(err, KErrNone); |
|
463 ::SetupTestDirectory(); |
|
464 |
|
465 ::DeleteDataFile(KTestDatabase); |
|
466 |
|
467 TRAP(err, ::RunTestsL()); |
|
468 TheDb.Close(); |
|
469 TheDbSession.Close(); |
|
470 TheFs.Close(); |
|
471 TEST2(err, KErrNone); |
|
472 |
|
473 ::DeleteDataFile(KTestDatabase); |
|
474 |
|
475 TheTest.End(); |
|
476 TheTest.Close(); |
|
477 |
|
478 delete trapCleanup; |
|
479 |
|
480 __UHEAP_MARKEND; |
|
481 |
|
482 return 0; |
|
483 } |
|
484 |
|
485 |