aboutsummaryrefslogtreecommitdiff
path: root/src/libs/dutil/test/DUtilUnitTest/SceUtilTest.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/libs/dutil/test/DUtilUnitTest/SceUtilTest.cpp')
-rw-r--r--src/libs/dutil/test/DUtilUnitTest/SceUtilTest.cpp488
1 files changed, 488 insertions, 0 deletions
diff --git a/src/libs/dutil/test/DUtilUnitTest/SceUtilTest.cpp b/src/libs/dutil/test/DUtilUnitTest/SceUtilTest.cpp
new file mode 100644
index 00000000..75b9222a
--- /dev/null
+++ b/src/libs/dutil/test/DUtilUnitTest/SceUtilTest.cpp
@@ -0,0 +1,488 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3#include "precomp.h"
4
5#include <sqlce_oledb.h>
6#include <sceutil.h>
7
8using namespace System;
9using namespace Xunit;
10using namespace WixTest;
11
12#define ASSIGN_INDEX_STRUCT(a, b, c) {a.wzName = c; a.rgColumns = b; a.cColumns = countof(b);};
13
14namespace DutilTests
15{
16 enum TABLES
17 {
18 TABLE_A,
19 TABLE_COUNT
20 };
21
22 enum TABLE_A_COLUMNS
23 {
24 TABLE_A_KEY,
25 TABLE_A_BINARY,
26 TABLE_A_DWORD,
27 TABLE_A_QWORD,
28 TABLE_A_BOOL,
29 TABLE_A_STRING,
30 TABLE_A_DWORD_NULLABLE,
31 TABLE_A_INITIAL_COLUMNS,
32
33 TABLE_A_EXTRA_STRING = TABLE_A_INITIAL_COLUMNS,
34 TABLE_A_FINAL_COLUMNS
35 };
36
37 struct TableARowValue
38 {
39 DWORD dwAutoGenKey;
40
41 BYTE *pbBinary;
42 DWORD cBinary;
43
44 DWORD dw;
45 DWORD64 qw;
46 BOOL f;
47 LPWSTR scz;
48
49 BOOL fNullablePresent;
50 DWORD dwNullable;
51
52 BOOL fSchemaV2;
53 LPWSTR sczExtra;
54 };
55
56 public ref class SceUtil
57 {
58 public:
59 void ReleaseSceSchema(SCE_DATABASE_SCHEMA *pdsSchema)
60 {
61 DWORD dwTable;
62
63 for (dwTable = 0; dwTable < pdsSchema->cTables; ++dwTable)
64 {
65 ReleaseNullMem(pdsSchema->rgTables[dwTable].rgColumns);
66 ReleaseNullMem(pdsSchema->rgTables[dwTable].rgIndexes);
67 }
68
69 ReleaseMem(pdsSchema->rgTables);
70
71 return;
72 }
73
74 void SetupSchema(SCE_DATABASE_SCHEMA *pSchema, BOOL fIncludeExtended)
75 {
76 pSchema->cTables = TABLE_COUNT;
77 pSchema->rgTables = static_cast<SCE_TABLE_SCHEMA*>(MemAlloc(TABLE_COUNT * sizeof(SCE_TABLE_SCHEMA), TRUE));
78 NativeAssert::True(pSchema->rgTables != NULL);
79
80 pSchema->rgTables[TABLE_A].wzName = L"TableA";
81 pSchema->rgTables[TABLE_A].cColumns = fIncludeExtended ? TABLE_A_FINAL_COLUMNS : TABLE_A_INITIAL_COLUMNS;
82 pSchema->rgTables[TABLE_A].cIndexes = 2;
83
84 for (DWORD i = 0; i < pSchema->cTables; ++i)
85 {
86 pSchema->rgTables[i].rgColumns = static_cast<SCE_COLUMN_SCHEMA*>(MemAlloc(sizeof(SCE_COLUMN_SCHEMA) * pSchema->rgTables[i].cColumns, TRUE));
87 NativeAssert::True(pSchema->rgTables[i].rgColumns != NULL);
88
89 pSchema->rgTables[i].rgIndexes = static_cast<SCE_INDEX_SCHEMA*>(MemAlloc(sizeof(SCE_COLUMN_SCHEMA) * pSchema->rgTables[i].cIndexes, TRUE));
90 NativeAssert::True(pSchema->rgTables[i].rgIndexes != NULL);
91 }
92
93 pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_KEY].wzName = L"Key";
94 pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_KEY].dbtColumnType = DBTYPE_I4;
95 pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_KEY].fPrimaryKey = TRUE;
96 pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_KEY].fAutoIncrement = TRUE;
97 pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_BINARY].wzName = L"Binary";
98 pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_BINARY].dbtColumnType = DBTYPE_BYTES;
99 pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_DWORD].wzName = L"Dword";
100 pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_DWORD].dbtColumnType = DBTYPE_I4;
101 pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_QWORD].wzName = L"Qword";
102 pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_QWORD].dbtColumnType = DBTYPE_I8;
103 pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_BOOL].wzName = L"Bool";
104 pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_BOOL].dbtColumnType = DBTYPE_BOOL;
105 pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_STRING].wzName = L"String";
106 pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_STRING].dbtColumnType = DBTYPE_WSTR;
107 pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_DWORD_NULLABLE].wzName = L"Nullable";
108 pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_DWORD_NULLABLE].dbtColumnType = DBTYPE_I4;
109 pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_DWORD_NULLABLE].fNullable = TRUE;
110
111 if (fIncludeExtended)
112 {
113 pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_EXTRA_STRING].wzName = L"ExtraString";
114 pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_EXTRA_STRING].dbtColumnType = DBTYPE_WSTR;
115 pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_EXTRA_STRING].fNullable = TRUE;
116 }
117
118 static DWORD rgdwTableA_Index1[] = { TABLE_A_DWORD, TABLE_A_STRING, TABLE_A_QWORD };
119 static DWORD rgdwTableA_Index2[] = { TABLE_A_DWORD, TABLE_A_STRING };
120
121 ASSIGN_INDEX_STRUCT(pSchema->rgTables[TABLE_A].rgIndexes[0], rgdwTableA_Index1, L"Dword_String_Qword");
122 ASSIGN_INDEX_STRUCT(pSchema->rgTables[TABLE_A].rgIndexes[1], rgdwTableA_Index2, L"Dword_String");
123 }
124
125 void SetStructValues(TableARowValue *pValue, BYTE *pbBinary, DWORD cBinary, DWORD dw, DWORD64 qw, BOOL f, LPWSTR scz, DWORD *pdw, LPWSTR sczExtra)
126 {
127 pValue->pbBinary = pbBinary;
128 pValue->cBinary = cBinary;
129 pValue->dw = dw;
130 pValue->qw = qw;
131 pValue->f = f;
132 pValue->scz = scz;
133
134 if (pdw)
135 {
136 pValue->fNullablePresent = TRUE;
137 pValue->dwNullable = *pdw;
138 }
139 else
140 {
141 pValue->fNullablePresent = FALSE;
142 }
143
144 if (sczExtra)
145 {
146 pValue->fSchemaV2 = TRUE;
147 pValue->sczExtra = sczExtra;
148 }
149 else
150 {
151 pValue->fSchemaV2 = FALSE;
152 }
153 }
154
155 void AssertStructValuesSame(TableARowValue *pValueExpected, TableARowValue *pValueOther)
156 {
157 NativeAssert::Equal(pValueExpected->cBinary, pValueOther->cBinary);
158 NativeAssert::True(0 == memcmp(pValueExpected->pbBinary, pValueOther->pbBinary, pValueOther->cBinary));
159
160 NativeAssert::Equal(pValueExpected->dw, pValueOther->dw);
161 NativeAssert::Equal(pValueExpected->qw, pValueOther->qw);
162 NativeAssert::Equal(pValueExpected->f, pValueOther->f);
163 NativeAssert::True(0 == wcscmp(pValueExpected->scz, pValueOther->scz));
164
165 NativeAssert::Equal(pValueExpected->fNullablePresent, pValueOther->fNullablePresent);
166 if (pValueExpected->fNullablePresent)
167 {
168 NativeAssert::Equal(pValueExpected->dwNullable, pValueOther->dwNullable);
169 }
170
171 NativeAssert::Equal(pValueExpected->fSchemaV2, pValueOther->fSchemaV2);
172 if (pValueExpected->fSchemaV2)
173 {
174 NativeAssert::True(0 == wcscmp(pValueExpected->sczExtra, pValueOther->sczExtra));
175 }
176 }
177
178 void InsertRow(SCE_DATABASE *pDatabase, TableARowValue *pValue, BOOL fRollback)
179 {
180 HRESULT hr = S_OK;
181 SCE_ROW_HANDLE sceRow = NULL;
182
183 hr = SceBeginTransaction(pDatabase);
184 NativeAssert::Succeeded(hr, "Failed to begin transaction");
185
186 hr = ScePrepareInsert(pDatabase, TABLE_A, &sceRow);
187 NativeAssert::Succeeded(hr, "Failed to prepare to insert row");
188
189 hr = SceSetColumnBinary(sceRow, TABLE_A_BINARY, pValue->pbBinary, pValue->cBinary);
190 NativeAssert::Succeeded(hr, "Failed to set binary value");
191
192 hr = SceSetColumnDword(sceRow, TABLE_A_DWORD, pValue->dw);
193 NativeAssert::Succeeded(hr, "Failed to set dword value");
194
195 hr = SceSetColumnQword(sceRow, TABLE_A_QWORD, pValue->qw);
196 NativeAssert::Succeeded(hr, "Failed to set qword value");
197
198 hr = SceSetColumnBool(sceRow, TABLE_A_BOOL, pValue->f);
199 NativeAssert::Succeeded(hr, "Failed to set bool value");
200
201 hr = SceSetColumnString(sceRow, TABLE_A_STRING, pValue->scz);
202 NativeAssert::Succeeded(hr, "Failed to set string value");
203
204 if (pValue->fNullablePresent)
205 {
206 hr = SceSetColumnDword(sceRow, TABLE_A_DWORD_NULLABLE, pValue->dwNullable);
207 NativeAssert::Succeeded(hr, "Failed to set dword value");
208 }
209 else
210 {
211 hr = SceSetColumnNull(sceRow, TABLE_A_DWORD_NULLABLE);
212 NativeAssert::Succeeded(hr, "Failed to set null value");
213 }
214
215 if (pValue->fSchemaV2)
216 {
217 hr = SceSetColumnString(sceRow, TABLE_A_EXTRA_STRING, pValue->sczExtra);
218 NativeAssert::Succeeded(hr, "Failed to set extra string value");
219 }
220
221 hr = SceFinishUpdate(sceRow);
222 NativeAssert::Succeeded(hr, "Failed to finish insert");
223
224 if (fRollback)
225 {
226 hr = SceRollbackTransaction(pDatabase);
227 NativeAssert::Succeeded(hr, "Failed to rollback transaction");
228 }
229 else
230 {
231 hr = SceCommitTransaction(pDatabase);
232 NativeAssert::Succeeded(hr, "Failed to commit transaction");
233
234 hr = SceGetColumnDword(sceRow, TABLE_A_KEY, &pValue->dwAutoGenKey);
235 NativeAssert::Succeeded(hr, "Failed to get autogen key after insert");
236
237 NativeAssert::True(pValue->dwAutoGenKey != 0);
238 }
239
240 ReleaseSceRow(sceRow);
241 }
242
243 void VerifyRow(TableARowValue *pExpectedValue, SCE_ROW_HANDLE sceRow)
244 {
245 HRESULT hr = S_OK;
246 TableARowValue value = {};
247
248 hr = SceGetColumnBinary(sceRow, TABLE_A_BINARY, &value.pbBinary, &value.cBinary);
249 NativeAssert::Succeeded(hr, "Failed to get binary value from result row");
250
251 hr = SceGetColumnDword(sceRow, TABLE_A_DWORD, &value.dw);
252 NativeAssert::Succeeded(hr, "Failed to get dword value from result row");
253
254 hr = SceGetColumnQword(sceRow, TABLE_A_QWORD, &value.qw);
255 NativeAssert::Succeeded(hr, "Failed to get qword value from result row");
256
257 hr = SceGetColumnBool(sceRow, TABLE_A_BOOL, &value.f);
258 NativeAssert::Succeeded(hr, "Failed to get bool value from result row");
259
260 hr = SceGetColumnString(sceRow, TABLE_A_STRING, &value.scz);
261 NativeAssert::Succeeded(hr, "Failed to get string value from result row");
262
263 hr = SceGetColumnDword(sceRow, TABLE_A_DWORD_NULLABLE, &value.dwNullable);
264 if (hr == E_NOTFOUND)
265 {
266 value.fNullablePresent = FALSE;
267 hr = S_OK;
268 }
269 else
270 {
271 NativeAssert::Succeeded(hr, "Failed to get string value from result row");
272 value.fNullablePresent = TRUE;
273 }
274
275 if (pExpectedValue->fSchemaV2)
276 {
277 value.fSchemaV2 = TRUE;
278 hr = SceGetColumnString(sceRow, TABLE_A_EXTRA_STRING, &value.sczExtra);
279 NativeAssert::Succeeded(hr, "Failed to get extra string value from result row");
280 }
281
282 AssertStructValuesSame(pExpectedValue, &value);
283
284 ReleaseNullMem(value.pbBinary);
285 ReleaseNullStr(value.scz);
286 }
287
288 void VerifyQuery(TableARowValue **rgExpectedValues, DWORD cExpectedValues, SCE_QUERY_RESULTS_HANDLE queryResults)
289 {
290 HRESULT hr = S_OK;
291 SCE_ROW_HANDLE sceRow = NULL;
292
293 for (DWORD i = 0; i < cExpectedValues; ++i)
294 {
295 hr = SceGetNextResultRow(queryResults, &sceRow);
296 NativeAssert::Succeeded(hr, "Failed to get next result row");
297
298 VerifyRow(rgExpectedValues[i], sceRow);
299 ReleaseNullSceRow(sceRow);
300 }
301
302 // No more results
303 NativeAssert::True(NULL == queryResults || FAILED(SceGetNextResultRow(queryResults, &sceRow)));
304 }
305
306 void TestIndex(SCE_DATABASE *pDatabase)
307 {
308 HRESULT hr = S_OK;
309 BYTE binary1[50] = { 0x80, 0x70 };
310 BYTE binary2[40] = { 0x90, 0xAB };
311 BYTE binary3[40] = { 0x85, 0x88 };
312 DWORD dwValue1 = 0x55555555, dwValue2 = 0x88888888;
313 TableARowValue value1 = {}, value2 = {}, value3 = {}, value4 = {}, value5 = {};
314 SCE_QUERY_HANDLE query = NULL;
315 SCE_QUERY_RESULTS_HANDLE results = NULL;
316
317 SetStructValues(&value1, static_cast<BYTE *>(binary1), sizeof(binary1), 3, 1, TRUE, L"zzz", &dwValue1, NULL);
318 SetStructValues(&value2, static_cast<BYTE *>(binary2), sizeof(binary2), 3, 2, TRUE, L"yyy", &dwValue2, NULL);
319 SetStructValues(&value3, static_cast<BYTE *>(binary3), sizeof(binary3), 3, 3, TRUE, L"xxx", NULL, NULL);
320 SetStructValues(&value4, static_cast<BYTE *>(binary2), sizeof(binary2), 4, 4, TRUE, L"xyz", &dwValue2, NULL);
321 SetStructValues(&value5, static_cast<BYTE *>(binary3), sizeof(binary3), 3, 1, TRUE, L"yyy", &dwValue2, NULL);
322
323 // Rollback an insert to confirm the insert doesn't happen and database can still be interacted with normally afterwards
324 InsertRow(pDatabase, &value1, TRUE);
325
326 InsertRow(pDatabase, &value1, FALSE);
327 InsertRow(pDatabase, &value2, FALSE);
328 InsertRow(pDatabase, &value3, FALSE);
329 InsertRow(pDatabase, &value4, FALSE);
330 InsertRow(pDatabase, &value5, FALSE);
331
332 NativeAssert::True(value1.dwAutoGenKey != value2.dwAutoGenKey);
333
334 // Test setting 1 column
335 hr = SceBeginQuery(pDatabase, TABLE_A, 0, &query);
336 NativeAssert::Succeeded(hr, "Failed to begin query");
337
338 hr = SceSetQueryColumnDword(query, 3);
339 NativeAssert::Succeeded(hr, "Failed to set query column dword");
340
341 hr = SceRunQueryRange(&query, &results);
342 NativeAssert::Succeeded(hr, "Failed to run query");
343 NativeAssert::True(query == NULL);
344
345 TableARowValue *sortedAfterQuery1[] = { &value3, &value5, &value2, &value1 };
346 VerifyQuery(sortedAfterQuery1, _countof(sortedAfterQuery1), results);
347 ReleaseNullSceQueryResults(results);
348
349 // Test setting 2 columns, third column is unspecified so results are sorted by it
350 hr = SceBeginQuery(pDatabase, TABLE_A, 0, &query);
351 NativeAssert::Succeeded(hr, "Failed to begin query");
352
353 hr = SceSetQueryColumnDword(query, 3);
354 NativeAssert::Succeeded(hr, "Failed to set query column dword");
355
356 hr = SceSetQueryColumnString(query, L"yyy");
357 NativeAssert::Succeeded(hr, "Failed to set query column dword");
358
359 hr = SceRunQueryRange(&query, &results);
360 NativeAssert::Succeeded(hr, "Failed to run query");
361 NativeAssert::True(query == NULL);
362
363 TableARowValue *sortedAfterQuery2[] = { &value5, &value2 };
364 VerifyQuery(sortedAfterQuery2, _countof(sortedAfterQuery2), results);
365 ReleaseNullSceQueryResults(results);
366
367 // Test setting 2 columns, third column of index is unspecified so results are sorted by it
368 hr = SceBeginQuery(pDatabase, TABLE_A, 0, &query);
369 NativeAssert::Succeeded(hr, "Failed to begin query");
370
371 hr = SceSetQueryColumnDword(query, 3);
372 NativeAssert::Succeeded(hr, "Failed to set query column dword");
373
374 hr = SceSetQueryColumnString(query, L"yyy");
375 NativeAssert::Succeeded(hr, "Failed to set query column dword");
376
377 hr = SceRunQueryRange(&query, &results);
378 NativeAssert::Succeeded(hr, "Failed to run query");
379 NativeAssert::True(query == NULL);
380
381 TableARowValue *sortedAfterQuery3[] = { &value5, &value2 };
382 VerifyQuery(sortedAfterQuery3, _countof(sortedAfterQuery3), results);
383 ReleaseNullSceQueryResults(results);
384
385 // Test setting 2 columns in a different (2 column) index, so there is no 3rd column in index to sort by
386 hr = SceBeginQuery(pDatabase, TABLE_A, 1, &query);
387 NativeAssert::Succeeded(hr, "Failed to begin query");
388
389 hr = SceSetQueryColumnDword(query, 3);
390 NativeAssert::Succeeded(hr, "Failed to set query column dword");
391
392 hr = SceSetQueryColumnString(query, L"yyy");
393 NativeAssert::Succeeded(hr, "Failed to set query column dword");
394
395 hr = SceRunQueryRange(&query, &results);
396 NativeAssert::Succeeded(hr, "Failed to run query");
397 NativeAssert::True(query == NULL);
398
399 TableARowValue *sortedAfterQuery4[] = { &value2, &value5 };
400 VerifyQuery(sortedAfterQuery4, _countof(sortedAfterQuery4), results);
401 ReleaseNullSceQueryResults(results);
402 }
403
404 void TestReadWriteSchemaV2(SCE_DATABASE *pDatabase)
405 {
406 HRESULT hr = S_OK;
407 BYTE binary1[40] = { 0x55, 0x44 };
408 DWORD dwValue1 = 58;
409 TableARowValue value1 = {};
410 SCE_QUERY_HANDLE query = NULL;
411 SCE_ROW_HANDLE row = NULL;
412
413 SetStructValues(&value1, static_cast<BYTE *>(binary1), sizeof(binary1), 5, 1, TRUE, L"zzz", &dwValue1, L"newextrastring");
414
415 InsertRow(pDatabase, &value1, FALSE);
416
417 // Test setting 1 column
418 hr = SceBeginQuery(pDatabase, TABLE_A, 0, &query);
419 NativeAssert::Succeeded(hr, "Failed to begin query");
420
421 hr = SceSetQueryColumnDword(query, 5);
422 NativeAssert::Succeeded(hr, "Failed to set query column dword");
423
424 hr = SceRunQueryExact(&query, &row);
425 NativeAssert::Succeeded(hr, "Failed to run query exact");
426
427 VerifyRow(&value1, row);
428 }
429
430 [Fact]
431 void SceUtilTest()
432 {
433 HRESULT hr = S_OK;
434 BOOL fComInitialized = FALSE;
435 LPWSTR sczDbPath = NULL;
436 SCE_DATABASE *pDatabase = NULL;
437 SCE_DATABASE_SCHEMA schema1 = {};
438 SCE_DATABASE_SCHEMA schema2 = {};
439
440 try
441 {
442 hr = ::CoInitialize(0);
443 NativeAssert::Succeeded(hr, "Failed to initialize COM");
444 fComInitialized = TRUE;
445
446 SetupSchema(&schema1, FALSE);
447 SetupSchema(&schema2, TRUE);
448
449 hr = PathExpand(&sczDbPath, L"%TEMP%\\SceUtilTest\\UnitTest.sdf", PATH_EXPAND_ENVIRONMENT);
450 NativeAssert::Succeeded(hr, "Failed to get path to test database");
451
452 FileEnsureDelete(sczDbPath);
453
454 hr = SceEnsureDatabase(sczDbPath, L"sqlceoledb40.dll", L"Test", 1, &schema1, &pDatabase);
455 NativeAssert::Succeeded(hr, "Failed to ensure database schema");
456
457 TestIndex(pDatabase);
458
459 hr = SceCloseDatabase(pDatabase);
460 pDatabase = NULL;
461 NativeAssert::Succeeded(hr, "Failed to close database");
462
463 // Add column to schema
464 hr = SceEnsureDatabase(sczDbPath, L"sqlceoledb40.dll", L"Test", 1, &schema2, &pDatabase);
465 NativeAssert::Succeeded(hr, "Failed to ensure database schema");
466
467 TestReadWriteSchemaV2(pDatabase);
468 }
469 finally
470 {
471 ReleaseSceSchema(&schema1);
472 ReleaseSceSchema(&schema2);
473
474 if (NULL != pDatabase)
475 {
476 hr = SceCloseDatabase(pDatabase);
477 NativeAssert::Succeeded(hr, "Failed to close database");
478 }
479 ReleaseStr(sczDbPath);
480
481 if (fComInitialized)
482 {
483 ::CoUninitialize();
484 }
485 }
486 }
487 };
488}