Branch data Line data Source code
1 : : #include "sute.h"
2 : : #include "db_upgrade.h"
3 : :
4 : : /**
5 : : * @brief Validate zero-converted CmpctStat values after migration fixups
6 : : *
7 : : * @param[in] stat Compact stat structure to verify
8 : : *
9 : : * @return Return status code:
10 : : * - SUCCESS: Structure matches expected zero-converted values
11 : : * - FAILURE: One or more fields differ from expected values
12 : : */
13 : 4 : static Return db_verify_zero_converted_cmpctstat(const CmpctStat *stat)
14 : : {
15 [ - + ]: 4 : if(stat == NULL)
16 : : {
17 : 0 : return FAILURE;
18 : : }
19 : :
20 [ - + ]: 4 : if(stat->st_size != 0)
21 : : {
22 : 0 : return FAILURE;
23 : : }
24 : :
25 [ - + ]: 4 : if(stat->st_blocks != BLKCNT_UNKNOWN)
26 : : {
27 : 0 : return FAILURE;
28 : : }
29 : :
30 [ + - - + ]: 4 : if(stat->st_dev != 0 || stat->st_ino != 0)
31 : : {
32 : 0 : return FAILURE;
33 : : }
34 : :
35 [ + - - + ]: 4 : if(stat->mtim_tv_sec != 0 || stat->mtim_tv_nsec != 0)
36 : : {
37 : 0 : return FAILURE;
38 : : }
39 : :
40 [ + - - + ]: 4 : if(stat->ctim_tv_sec != 0 || stat->ctim_tv_nsec != 0)
41 : : {
42 : 0 : return FAILURE;
43 : : }
44 : :
45 : 4 : return SUCCESS;
46 : : }
47 : :
48 : : /**
49 : : * @brief Create trigger that aborts on second stat update in files table
50 : : *
51 : : * @param[in] db_filename Database filename relative to TMPDIR
52 : : *
53 : : * @return Return status code:
54 : : * - SUCCESS: Trigger and helper table were created
55 : : * - FAILURE: Validation, DB open, or SQL execution failed
56 : : */
57 : 2 : static Return db_create_abort_on_second_stat_update_trigger(const char *db_filename)
58 : : {
59 : : /* Status returned by this function through provide()
60 : : Default value assumes successful completion */
61 : 2 : Return status = SUCCESS;
62 : 2 : sqlite3 *db = NULL;
63 : 2 : const char *sql =
64 : : "DROP TRIGGER IF EXISTS __test_abort_on_second_update;"
65 : : "DROP TABLE IF EXISTS __test_fail_counter;"
66 : : "CREATE TABLE __test_fail_counter(n INTEGER NOT NULL);"
67 : : "INSERT INTO __test_fail_counter(n) VALUES(0);"
68 : : "CREATE TRIGGER __test_abort_on_second_update "
69 : : "BEFORE UPDATE OF stat ON files "
70 : : "BEGIN "
71 : : "UPDATE __test_fail_counter SET n = n + 1;"
72 : : "SELECT CASE WHEN (SELECT n FROM __test_fail_counter LIMIT 1) >= 2 "
73 : : "THEN RAISE(ABORT,'forced rollback test failure') END;"
74 : : "END;";
75 : :
76 [ - + ]: 2 : if(db_filename == NULL)
77 : : {
78 : 0 : status = FAILURE;
79 : : }
80 : :
81 [ + - ]: 2 : if(SUCCESS == status)
82 : : {
83 : 2 : status = open_db_from_tmpdir(db_filename,SQLITE_OPEN_READWRITE,&db);
84 : : }
85 : :
86 [ + - - + ]: 2 : if(SUCCESS == status && SQLITE_OK != sqlite3_exec(db,sql,NULL,NULL,NULL))
87 : : {
88 : 0 : status = FAILURE;
89 : : }
90 : :
91 : 2 : (void)sqlite3_close(db);
92 : :
93 : 2 : return(status);
94 : : }
95 : :
96 : : /**
97 : : * @brief Reject using a legacy v0 DB as the primary database without --update
98 : : *
99 : : * Verify the run fails and prints the expected warning for the upgrade path
100 : : */
101 : 2 : Return test0015_1(void)
102 : : {
103 : 2 : INITTEST;
104 : :
105 : 2 : ASSERT(SUCCESS == set_environment_variable("TESTING","true"));
106 : 2 : ASSERT(SUCCESS == copy_path("tests/templates/0015_database_v0.db","0015_database_v0.db"));
107 : :
108 : 2 : const char *arguments = "--database=./0015_database_v0.db tests/fixtures/diffs/diff1";
109 : :
110 : 2 : m_create(char,result,MEMORY_STRING);
111 : 2 : m_create(char,pattern,MEMORY_STRING);
112 : :
113 : 2 : const char *filename = "templates/0015_001.txt";
114 : :
115 : 2 : ASSERT(SUCCESS == runit(arguments,result,NULL,WARNING,ALLOW_BOTH));
116 : :
117 : 2 : ASSERT(SUCCESS == get_file_content(filename,pattern));
118 : :
119 : : // Match the result against the pattern
120 : 2 : ASSERT(SUCCESS == match_pattern(result,pattern,filename));
121 : :
122 : : // Clean to use it iteratively
123 : 2 : m_del(pattern);
124 : 2 : m_del(result);
125 : :
126 : : // Clean up test results
127 : 2 : ASSERT(SUCCESS == delete_path("0015_database_v0.db"));
128 : :
129 : 2 : RETURN_STATUS;
130 : : }
131 : :
132 : : /**
133 : : * @brief Upgrade a legacy v0 DB as the primary database with --update
134 : : *
135 : : * Verify the upgrade succeeds and produces the expected output
136 : : */
137 : 2 : Return test0015_2(void)
138 : : {
139 : 2 : INITTEST;
140 : 2 : ASSERT(SUCCESS == copy_path("tests/templates/0015_database_v0.db","0015_database_v0.db"));
141 : :
142 : 2 : ASSERT(SUCCESS == set_environment_variable("TESTING","true"));
143 : :
144 : 2 : const char *arguments = "--update --database=0015_database_v0.db "
145 : : "tests/fixtures/diffs/diff1";
146 : :
147 : 2 : m_create(char,result,MEMORY_STRING);
148 : 2 : m_create(char,pattern,MEMORY_STRING);
149 : :
150 : 2 : const char *filename = "templates/0015_002_1.txt";
151 : :
152 : 2 : ASSERT(SUCCESS == runit(arguments,result,NULL,COMPLETED,ALLOW_BOTH));
153 : :
154 : 2 : ASSERT(SUCCESS == get_file_content(filename,pattern));
155 : :
156 : : // Match the result against the pattern
157 : 2 : ASSERT(SUCCESS == match_pattern(result,pattern,filename));
158 : :
159 : : // Clean up test results
160 : 2 : ASSERT(SUCCESS == delete_path("0015_database_v0.db"));
161 : :
162 : : // Clean to use it iteratively
163 : 2 : m_del(pattern);
164 : 2 : m_del(result);
165 : :
166 : 2 : RETURN_STATUS;
167 : : }
168 : :
169 : : /**
170 : : * @brief Upgrade a legacy v0 DB with --watch-timestamps enabled
171 : : *
172 : : * Verify the upgrade succeeds and reports timestamp details in the output
173 : : */
174 : 2 : Return test0015_3(void)
175 : : {
176 : 2 : INITTEST;
177 : 2 : ASSERT(SUCCESS == copy_path("tests/templates/0015_database_v0.db","0015_database_v0.db"));
178 : :
179 : 2 : ASSERT(SUCCESS == set_environment_variable("TESTING","true"));
180 : :
181 : 2 : const char *arguments = "--watch-timestamps --update --database=0015_database_v0.db "
182 : : "tests/fixtures/diffs/diff1";
183 : :
184 : 2 : m_create(char,result,MEMORY_STRING);
185 : 2 : m_create(char,pattern,MEMORY_STRING);
186 : :
187 : 2 : const char *filename = "templates/0015_002_2.txt";
188 : :
189 : 2 : ASSERT(SUCCESS == runit(arguments,result,NULL,COMPLETED,ALLOW_BOTH));
190 : :
191 : 2 : ASSERT(SUCCESS == get_file_content(filename,pattern));
192 : :
193 : : // Match the result against the pattern
194 : 2 : ASSERT(SUCCESS == match_pattern(result,pattern,filename));
195 : :
196 : : // Clean to use it iteratively
197 : 2 : m_del(pattern);
198 : 2 : m_del(result);
199 : :
200 : 2 : RETURN_STATUS;
201 : : }
202 : :
203 : : /**
204 : : * @brief Re-run upgrade for the already upgraded v0 DB
205 : : *
206 : : * Verify the database is treated as current and the run stays successful
207 : : */
208 : 2 : Return test0015_4(void)
209 : : {
210 : 2 : INITTEST;
211 : :
212 : 2 : m_create(char,result,MEMORY_STRING);
213 : 2 : m_create(char,pattern,MEMORY_STRING);
214 : :
215 : 2 : const char *filename = "templates/0015_003.txt";
216 : :
217 : 2 : ASSERT(SUCCESS == set_environment_variable("TESTING","true"));
218 : :
219 : 2 : const char *arguments = "--update --database=./0015_database_v0.db "
220 : : "tests/fixtures/diffs/diff1";
221 : :
222 : 2 : ASSERT(SUCCESS == runit(arguments,result,NULL,COMPLETED,ALLOW_BOTH));
223 : :
224 : 2 : ASSERT(SUCCESS == get_file_content(filename,pattern));
225 : :
226 : : // Match the result against the pattern
227 : 2 : ASSERT(SUCCESS == match_pattern(result,pattern,filename));
228 : :
229 : : // Clean to use it iteratively
230 : 2 : m_del(pattern);
231 : 2 : m_del(result);
232 : :
233 : : // Clean up test results
234 : 2 : ASSERT(SUCCESS == delete_path("0015_database_v0.db"));
235 : :
236 : 2 : RETURN_STATUS;
237 : : }
238 : :
239 : : /**
240 : : * @brief Create a fresh DB with the default generated filename
241 : : *
242 : : * Verify the application reports the generated DB name in the expected output
243 : : */
244 : 2 : Return test0015_5(void)
245 : : {
246 : 2 : INITTEST;
247 : :
248 : 2 : ASSERT(SUCCESS == set_environment_variable("TESTING","true"));
249 : :
250 : : // Run the application without an explicit DB name
251 : 2 : const char *arguments = "tests/fixtures/diffs/diff1";
252 : :
253 : 2 : const char *filename = "templates/0015_004.txt"; // File name
254 : 2 : const char *template = "%DB_NAME%";
255 : :
256 : 2 : const char *replacement = getenv("DBNAME"); // Database name
257 : :
258 : 2 : ASSERT(replacement != NULL);
259 : :
260 : 2 : ASSERT(SUCCESS == match_app_output(arguments,filename,template,replacement,COMPLETED));
261 : :
262 : 2 : RETURN_STATUS;
263 : : }
264 : :
265 : : /**
266 : : * @brief Compare a current DB against a legacy v0 DB without --update
267 : : *
268 : : * Verify the application warns that the legacy DB must be upgraded first
269 : : */
270 : 2 : Return test0015_6(void)
271 : : {
272 : 2 : INITTEST;
273 : :
274 : 2 : ASSERT(SUCCESS == set_environment_variable("TESTING","true"));
275 : :
276 : 2 : ASSERT(SUCCESS == copy_path("tests/templates/0015_database_v0.db","0015_database_v0.db"));
277 : :
278 : 2 : const char *arguments = "--compare $DBNAME 0015_database_v0.db";
279 : :
280 : 2 : const char *filename = "templates/0015_005.txt"; // File name
281 : 2 : const char *template = "%DB_NAME%";
282 : :
283 : 2 : const char *replacement = getenv("DBNAME"); // Database name
284 : :
285 : 2 : ASSERT(replacement != NULL);
286 : :
287 : 2 : ASSERT(SUCCESS == match_app_output(arguments,filename,template,replacement,WARNING));
288 : :
289 : 2 : RETURN_STATUS;
290 : : }
291 : :
292 : : /**
293 : : * @brief Compare and upgrade a legacy v0 DB with --compare --update
294 : : *
295 : : * Verify the legacy DB is upgraded during comparison and the run completes
296 : : */
297 : 2 : Return test0015_7(void)
298 : : {
299 : 2 : INITTEST;
300 : :
301 : 2 : ASSERT(SUCCESS == set_environment_variable("TESTING","true"));
302 : :
303 : : // Run the comparison and upgrade flow
304 : 2 : const char *arguments = "--compare --update $DBNAME 0015_database_v0.db";
305 : :
306 : 2 : const char *filename = "templates/0015_006.txt"; // File name
307 : 2 : const char *template = "%DB_NAME%";
308 : :
309 : 2 : const char *replacement = getenv("DBNAME"); // Database name
310 : :
311 : 2 : ASSERT(replacement != NULL);
312 : :
313 : 2 : ASSERT(SUCCESS == match_app_output(arguments,filename,template,replacement,COMPLETED));
314 : :
315 : : // Clean up test results
316 : 2 : ASSERT(SUCCESS == delete_path("0015_database_v0.db"));
317 : :
318 : 2 : RETURN_STATUS;
319 : : }
320 : :
321 : : /**
322 : : * @brief Upgrade a legacy v1 DB as the primary database with --update
323 : : *
324 : : * Verify the upgrade succeeds and produces the expected output
325 : : */
326 : 2 : Return test0015_8(void)
327 : : {
328 : 2 : INITTEST;
329 : :
330 : 2 : ASSERT(SUCCESS == set_environment_variable("TESTING","true"));
331 : 2 : ASSERT(SUCCESS == copy_path("tests/templates/0015_database_v1.db","0015_database_v1.db"));
332 : :
333 : 2 : const char *arguments = "--update --database=0015_database_v1.db "
334 : : "tests/fixtures/diffs/diff1";
335 : :
336 : 2 : m_create(char,result,MEMORY_STRING);
337 : 2 : m_create(char,pattern,MEMORY_STRING);
338 : :
339 : 2 : const char *filename = "templates/0015_007.txt";
340 : :
341 : 2 : ASSERT(SUCCESS == runit(arguments,result,NULL,COMPLETED,ALLOW_BOTH));
342 : :
343 : 2 : ASSERT(SUCCESS == get_file_content(filename,pattern));
344 : :
345 : : // Match the result against the pattern
346 : 2 : ASSERT(SUCCESS == match_pattern(result,pattern,filename));
347 : :
348 : : // Clean to use it iteratively
349 : 2 : m_del(pattern);
350 : 2 : m_del(result);
351 : :
352 : 2 : RETURN_STATUS;
353 : : }
354 : :
355 : : /**
356 : : * @brief Re-run upgrade for the already upgraded v1 DB
357 : : *
358 : : * Verify the database is treated as current and the run stays successful
359 : : */
360 : 2 : Return test0015_9(void)
361 : : {
362 : 2 : INITTEST;
363 : :
364 : 2 : m_create(char,result,MEMORY_STRING);
365 : 2 : m_create(char,pattern,MEMORY_STRING);
366 : :
367 : 2 : const char *filename = "templates/0015_008.txt";
368 : :
369 : 2 : ASSERT(SUCCESS == set_environment_variable("TESTING","true"));
370 : :
371 : 2 : const char *arguments = "--update --database=./0015_database_v1.db "
372 : : "tests/fixtures/diffs/diff1";
373 : :
374 : 2 : ASSERT(SUCCESS == runit(arguments,result,NULL,COMPLETED,ALLOW_BOTH));
375 : :
376 : 2 : ASSERT(SUCCESS == get_file_content(filename,pattern));
377 : :
378 : : // Match the result against the pattern
379 : 2 : ASSERT(SUCCESS == match_pattern(result,pattern,filename));
380 : :
381 : : // Clean to use it iteratively
382 : 2 : m_del(pattern);
383 : 2 : m_del(result);
384 : :
385 : : // Clean up test results
386 : 2 : ASSERT(SUCCESS == delete_path("0015_database_v1.db"));
387 : :
388 : 2 : RETURN_STATUS;
389 : : }
390 : :
391 : : /**
392 : : * @brief Compare and upgrade a legacy v1 DB with --compare --update
393 : : *
394 : : * Verify the legacy DB is upgraded during comparison and the run completes
395 : : */
396 : 2 : Return test0015_10(void)
397 : : {
398 : 2 : INITTEST;
399 : :
400 : 2 : ASSERT(SUCCESS == set_environment_variable("TESTING","true"));
401 : :
402 : : // Run the comparison and upgrade flow
403 : 2 : ASSERT(SUCCESS == copy_path("tests/templates/0015_database_v1.db","0015_database_v1.db"));
404 : :
405 : 2 : const char *arguments = "--compare --update $DBNAME 0015_database_v1.db";
406 : :
407 : 2 : const char *filename = "templates/0015_009.txt"; // File name
408 : 2 : const char *template = "%DB_NAME%";
409 : :
410 : 2 : const char *replacement = getenv("DBNAME"); // Database name
411 : :
412 : 2 : ASSERT(replacement != NULL);
413 : :
414 : 2 : ASSERT(SUCCESS == match_app_output(arguments,filename,template,replacement,COMPLETED));
415 : :
416 : : // Clean up test results
417 : 2 : ASSERT(SUCCESS == delete_path("0015_database_v1.db"));
418 : :
419 : 2 : RETURN_STATUS;
420 : : }
421 : :
422 : : /**
423 : : * @brief Upgrade a legacy v2 DB as the primary database with --update
424 : : *
425 : : * Verify the upgrade succeeds and prints the verbose migration details
426 : : */
427 : 2 : Return test0015_11(void)
428 : : {
429 : 2 : INITTEST;
430 : :
431 : 2 : ASSERT(SUCCESS == set_environment_variable("TESTING","false"));
432 : 2 : ASSERT(SUCCESS == copy_path("tests/templates/0015_database_v2.db","0015_database_v2.db"));
433 : :
434 : 2 : const char *arguments = "--update --database=0015_database_v2.db --verbose "
435 : : "tests/fixtures/diffs/diff1";
436 : :
437 : 2 : m_create(char,result,MEMORY_STRING);
438 : 2 : m_create(char,pattern,MEMORY_STRING);
439 : :
440 : 2 : ASSERT(SUCCESS == runit(arguments,result,NULL,COMPLETED,ALLOW_BOTH));
441 : :
442 : 2 : const char *filename = "templates/0015_010.txt";
443 : :
444 : 2 : ASSERT(SUCCESS == get_file_content(filename,pattern));
445 : :
446 : : // Match the result against the pattern
447 : 2 : ASSERT(SUCCESS == match_pattern(result,pattern,filename));
448 : :
449 : : // Clean up test results
450 : 2 : ASSERT(SUCCESS == delete_path("0015_database_v2.db"));
451 : :
452 : : // Clean to use it iteratively
453 : 2 : m_del(pattern);
454 : 2 : m_del(result);
455 : :
456 : 2 : RETURN_STATUS;
457 : : }
458 : :
459 : : /**
460 : : * @brief Compare and upgrade a legacy v2 DB with --compare --update
461 : : *
462 : : * Verify the legacy DB is upgraded during comparison and the run completes
463 : : */
464 : 2 : Return test0015_12(void)
465 : : {
466 : 2 : INITTEST;
467 : :
468 : 2 : ASSERT(SUCCESS == set_environment_variable("TESTING","true"));
469 : :
470 : : // Run the comparison and upgrade flow
471 : 2 : ASSERT(SUCCESS == copy_path("tests/templates/0015_database_v2.db","0015_database_v2.db"));
472 : :
473 : 2 : const char *arguments = "--compare --update $DBNAME 0015_database_v2.db";
474 : :
475 : 2 : const char *filename = "templates/0015_011.txt"; // File name
476 : 2 : const char *template = "%DB_NAME%";
477 : :
478 : 2 : const char *replacement = getenv("DBNAME"); // Database name
479 : :
480 : 2 : ASSERT(replacement != NULL);
481 : :
482 : 2 : ASSERT(SUCCESS == match_app_output(arguments,filename,template,replacement,COMPLETED));
483 : :
484 : : // Clean up test results
485 : 2 : ASSERT(SUCCESS == delete_path(replacement));
486 : 2 : ASSERT(SUCCESS == delete_path("0015_database_v2.db"));
487 : :
488 : 2 : RETURN_STATUS;
489 : : }
490 : :
491 : : /**
492 : : * @brief Upgrade a legacy v3 DB whose filename contains UTF-8 characters
493 : : *
494 : : * Verify the primary DB upgrade works with spaces and non-ASCII characters
495 : : */
496 : 2 : Return test0015_13(void)
497 : : {
498 : 2 : INITTEST;
499 : :
500 : 2 : ASSERT(SUCCESS == set_environment_variable("TESTING","true"));
501 : :
502 : 2 : ASSERT(SUCCESS == copy_path("tests/templates/0015_database_v3 это база данных с пробелами и символами UTF-8.db","0015_database_v3 это база данных с пробелами и символами UTF-8.db"));
503 : :
504 : 2 : const char *arguments = "--update --database=\"0015_database_v3 это база данных с пробелами и символами UTF-8.db\" "
505 : : "tests/fixtures/diffs/diff1";
506 : :
507 : 2 : m_create(char,result,MEMORY_STRING);
508 : :
509 : 2 : ASSERT(SUCCESS == runit(arguments,result,NULL,COMPLETED,ALLOW_BOTH));
510 : :
511 : 2 : const char *filename = "templates/0015_012.txt";
512 : :
513 : 2 : m_create(char,pattern,MEMORY_STRING);
514 : :
515 : 2 : ASSERT(SUCCESS == get_file_content(filename,pattern));
516 : :
517 : : // Match the result against the pattern
518 : 2 : ASSERT(SUCCESS == match_pattern(result,pattern,filename));
519 : :
520 : : // Clean up test results
521 : 2 : ASSERT(SUCCESS == delete_path("0015_database_v3 это база данных с пробелами и символами UTF-8.db"));
522 : :
523 : : // Clean to use it iteratively
524 : 2 : m_del(pattern);
525 : 2 : m_del(result);
526 : :
527 : 2 : RETURN_STATUS;
528 : : }
529 : :
530 : : /**
531 : : * @brief Compare and upgrade a legacy v3 UTF-8 DB with --compare --update
532 : : *
533 : : * Verify the upgrade path works when both compared DB filenames contain UTF-8
534 : : */
535 : 2 : Return test0015_14(void)
536 : : {
537 : 2 : INITTEST;
538 : :
539 : 2 : ASSERT(SUCCESS == set_environment_variable("TESTING","true"));
540 : :
541 : 2 : ASSERT(SUCCESS == copy_path("tests/templates/0015_database_v3 это база данных с пробелами и символами UTF-8.db","0015_database_v3 это база данных с пробелами и символами UTF-8.db"));
542 : :
543 : 2 : ASSERT(SUCCESS == copy_path("tests/templates/0015_database_v4 это база данных с пробелами и символами UTF-8.db","0015_database_v4 это база данных с пробелами и символами UTF-8.db"));
544 : :
545 : 2 : const char *arguments = "--compare --update "
546 : : "\"0015_database_v3 это база данных с пробелами и символами UTF-8.db\" "
547 : : "\"0015_database_v4 это база данных с пробелами и символами UTF-8.db\"";
548 : :
549 : 2 : m_create(char,result,MEMORY_STRING);
550 : 2 : m_create(char,pattern,MEMORY_STRING);
551 : :
552 : 2 : ASSERT(SUCCESS == runit(arguments,result,NULL,COMPLETED,ALLOW_BOTH));
553 : :
554 : 2 : const char *filename = "templates/0015_013.txt";
555 : :
556 : 2 : ASSERT(SUCCESS == get_file_content(filename,pattern));
557 : :
558 : : // Match the result against the pattern
559 : 2 : ASSERT(SUCCESS == match_pattern(result,pattern,filename));
560 : :
561 : 2 : m_del(result);
562 : 2 : m_del(pattern);
563 : :
564 : 2 : ASSERT(SUCCESS == delete_path("0015_database_v3 это база данных с пробелами и символами UTF-8.db"));
565 : 2 : ASSERT(SUCCESS == delete_path("0015_database_v4 это база данных с пробелами и символами UTF-8.db"));
566 : :
567 : 2 : RETURN_STATUS;
568 : : }
569 : :
570 : : /**
571 : : * @brief Compare a fresh UTF-8 DB against a legacy UTF-8 v4 reference DB
572 : : *
573 : : * Verify the application can create, read, and compare DB filenames with
574 : : * spaces and non-ASCII characters while keeping checksum compatibility
575 : : */
576 : 2 : Return test0015_15(void)
577 : : {
578 : 2 : INITTEST;
579 : :
580 : 2 : ASSERT(SUCCESS == set_environment_variable("TESTING","true"));
581 : :
582 : 2 : ASSERT(SUCCESS == copy_path("tests/templates/0015_database_v4 это база данных с пробелами и символами UTF-8.db","0015_database_v4 это база данных с пробелами и символами UTF-8.db"));
583 : :
584 : 2 : m_create(char,pattern,MEMORY_STRING);
585 : 2 : m_create(char,result,MEMORY_STRING);
586 : 2 : m_create(char,chunk,MEMORY_STRING);
587 : :
588 : 2 : const char *arguments = "--database=\"Это новая база данных.db\" "
589 : : "tests/fixtures/diffs/diff1";
590 : :
591 : 2 : ASSERT(SUCCESS == runit(arguments,chunk,NULL,COMPLETED,ALLOW_BOTH));
592 : 2 : ASSERT(SUCCESS == m_copy(result,chunk));
593 : :
594 : 2 : arguments = "--compare \"Это новая база данных.db\" "
595 : : "\"0015_database_v4 это база данных с пробелами и символами UTF-8.db\"";
596 : :
597 : 2 : ASSERT(SUCCESS == runit(arguments,chunk,NULL,COMPLETED,ALLOW_BOTH));
598 : 2 : ASSERT(SUCCESS == m_concat_strings(result,chunk));
599 : :
600 : 2 : const char *filename = "templates/0015_014.txt";
601 : :
602 : 2 : ASSERT(SUCCESS == get_file_content(filename,pattern));
603 : :
604 : : // Match the result against the pattern
605 : 2 : ASSERT(SUCCESS == match_pattern(result,pattern,filename));
606 : :
607 : : // Clean to use it iteratively
608 : 2 : m_del(pattern);
609 : 2 : m_del(result);
610 : 2 : m_del(chunk);
611 : :
612 : : // Clean up test results
613 : 2 : ASSERT(SUCCESS == delete_path("Это новая база данных.db"));
614 : 2 : ASSERT(SUCCESS == delete_path("0015_database_v4 это база данных с пробелами и символами UTF-8.db"));
615 : :
616 : 2 : RETURN_STATUS;
617 : : }
618 : :
619 : : /**
620 : : * @brief Upgrade a v0 DB with one corrupted legacy stat blob
621 : : *
622 : : * Verify the full upgrade still completes and the corrupted row is converted
623 : : * into the expected zero-source compact stat representation
624 : : */
625 : 2 : Return test0015_16(void)
626 : : {
627 : 2 : INITTEST;
628 : 2 : const char *corrupted_db_filename = "0015_database_v0_corrupt.db";
629 : 2 : const char *reference_db_filename = "0015_database_v4_reference.db";
630 : :
631 : 2 : ASSERT(SUCCESS == set_environment_variable("TESTING","true"));
632 : 2 : ASSERT(SUCCESS == copy_path("tests/templates/0015_database_v0.db",corrupted_db_filename));
633 : 2 : ASSERT(SUCCESS == copy_path("tests/templates/0015_database_v4 это база данных с пробелами и символами UTF-8.db",reference_db_filename));
634 : :
635 : 2 : sqlite3_int64 row_id = 0;
636 : :
637 : 2 : ASSERT(SUCCESS == db_corrupt_first_row_stat_blob(corrupted_db_filename,&row_id));
638 : :
639 : 2 : m_create(char,result,MEMORY_STRING);
640 : 2 : m_create(char,pattern,MEMORY_STRING);
641 : :
642 : 2 : const char *arguments = "--compare --update 0015_database_v4_reference.db 0015_database_v0_corrupt.db";
643 : :
644 : 2 : ASSERT(SUCCESS == runit(arguments,result,NULL,COMPLETED,ALLOW_BOTH));
645 : 2 : const char *filename = "templates/0015_015.txt";
646 : 2 : ASSERT(SUCCESS == get_file_content(filename,pattern));
647 : 2 : ASSERT(SUCCESS == match_pattern(result,pattern,filename));
648 : :
649 : 2 : int db_version = 0;
650 : 2 : ASSERT(SUCCESS == read_db_version_from_metadata(corrupted_db_filename,&db_version));
651 : 2 : ASSERT(db_version == 4);
652 : :
653 : 2 : CmpctStat stat = {0};
654 : 2 : ASSERT(SUCCESS == db_read_cmpctstat_by_row_id(corrupted_db_filename,row_id,&stat));
655 : 2 : ASSERT(SUCCESS == db_verify_zero_converted_cmpctstat(&stat));
656 : :
657 : 2 : ASSERT(SUCCESS == delete_path(corrupted_db_filename));
658 : 2 : ASSERT(SUCCESS == delete_path(reference_db_filename));
659 : :
660 : 2 : m_del(result);
661 : 2 : m_del(pattern);
662 : :
663 : 2 : RETURN_STATUS;
664 : : }
665 : :
666 : : /**
667 : : * @brief Upgrade a v3 DB with one corrupted legacy stat blob
668 : : *
669 : : * Verify the full upgrade still completes and the corrupted row is stored
670 : : * using the zero-source conversion logic
671 : : */
672 : 2 : Return test0015_17(void)
673 : : {
674 : 2 : INITTEST;
675 : 2 : const char *corrupted_db_filename = "0015_database_v3_corrupt.db";
676 : 2 : const char *reference_db_filename = "0015_database_v4_reference.db";
677 : :
678 : 2 : ASSERT(SUCCESS == set_environment_variable("TESTING","true"));
679 : 2 : ASSERT(SUCCESS == copy_path("tests/templates/0015_database_v3 это база данных с пробелами и символами UTF-8.db",corrupted_db_filename));
680 : 2 : ASSERT(SUCCESS == copy_path("tests/templates/0015_database_v4 это база данных с пробелами и символами UTF-8.db",reference_db_filename));
681 : :
682 : 2 : sqlite3_int64 row_id = 0;
683 : 2 : ASSERT(SUCCESS == db_corrupt_first_row_stat_blob(corrupted_db_filename,&row_id));
684 : :
685 : 2 : const char *arguments = "--compare --update 0015_database_v4_reference.db 0015_database_v3_corrupt.db";
686 : :
687 : 2 : m_create(char,result,MEMORY_STRING);
688 : 2 : m_create(char,pattern,MEMORY_STRING);
689 : :
690 : 2 : ASSERT(SUCCESS == runit(arguments,result,NULL,COMPLETED,ALLOW_BOTH));
691 : 2 : const char *filename = "templates/0015_016.txt";
692 : 2 : ASSERT(SUCCESS == get_file_content(filename,pattern));
693 : 2 : ASSERT(SUCCESS == match_pattern(result,pattern,filename));
694 : :
695 : 2 : int db_version = 0;
696 : 2 : ASSERT(SUCCESS == read_db_version_from_metadata(corrupted_db_filename,&db_version));
697 : 2 : ASSERT(db_version == 4);
698 : :
699 : 2 : CmpctStat stat = {0};
700 : 2 : ASSERT(SUCCESS == db_read_cmpctstat_by_row_id(corrupted_db_filename,row_id,&stat));
701 : 2 : ASSERT(SUCCESS == db_verify_zero_converted_cmpctstat(&stat));
702 : :
703 : 2 : ASSERT(SUCCESS == delete_path(corrupted_db_filename));
704 : 2 : ASSERT(SUCCESS == delete_path(reference_db_filename));
705 : :
706 : 2 : m_del(result);
707 : 2 : m_del(pattern);
708 : :
709 : 2 : RETURN_STATUS;
710 : : }
711 : :
712 : : /**
713 : : * @brief Roll back 3->4 migration when SQLite fails during stat conversion
714 : : *
715 : : * Verify the transaction is rolled back and the DB content stays unchanged
716 : : */
717 : 2 : Return test0015_18(void)
718 : : {
719 : 2 : INITTEST;
720 : 2 : const char *rollback_db_filename = "0015_database_v3_rollback.db";
721 : 2 : const char *reference_db_filename = "0015_database_v4_reference.db";
722 : :
723 : 2 : ASSERT(SUCCESS == set_environment_variable("TESTING","true"));
724 : 2 : ASSERT(SUCCESS == copy_path("tests/templates/0015_database_v3 это база данных с пробелами и символами UTF-8.db",rollback_db_filename));
725 : 2 : ASSERT(SUCCESS == copy_path("tests/templates/0015_database_v4 это база данных с пробелами и символами UTF-8.db",reference_db_filename));
726 : :
727 : 2 : int files_count = 0;
728 : 2 : ASSERT(SUCCESS == db_read_files_count(rollback_db_filename,&files_count));
729 : 2 : ASSERT(files_count >= 2);
730 : :
731 : 2 : sqlite3_int64 row_id = 0;
732 : 2 : ASSERT(SUCCESS == db_read_first_row_id(rollback_db_filename,&row_id));
733 : :
734 : : unsigned char before_blob[512];
735 : 2 : int before_blob_size = 0;
736 : 2 : ASSERT(SUCCESS == db_read_stat_blob_by_row_id(rollback_db_filename,
737 : : row_id,
738 : : before_blob,
739 : : sizeof(before_blob),
740 : : &before_blob_size));
741 : :
742 : 2 : int v1_rows_before = 0;
743 : 2 : ASSERT(SUCCESS == db_read_files_count_with_blob_size(rollback_db_filename,
744 : : (int)sizeof(CmpctStat_v1),
745 : : &v1_rows_before));
746 : :
747 : 2 : ASSERT(SUCCESS == db_create_abort_on_second_stat_update_trigger(rollback_db_filename));
748 : :
749 : 2 : const char *arguments = "--compare --update 0015_database_v4_reference.db 0015_database_v3_rollback.db";
750 : :
751 : 2 : m_create(char,result,MEMORY_STRING);
752 : 2 : m_create(char,pattern,MEMORY_STRING);
753 : :
754 : 2 : ASSERT(SUCCESS == runit(arguments,result,NULL,FAILURE,ALLOW_BOTH));
755 : 2 : const char *filename = "templates/0015_017.txt";
756 : 2 : ASSERT(SUCCESS == get_file_content(filename,pattern));
757 : 2 : ASSERT(SUCCESS == match_pattern(result,pattern,filename));
758 : :
759 : 2 : int db_version = 0;
760 : 2 : ASSERT(SUCCESS == read_db_version_from_metadata(rollback_db_filename,&db_version));
761 : 2 : ASSERT(db_version == 3);
762 : :
763 : : unsigned char after_blob[512];
764 : 2 : int after_blob_size = 0;
765 : 2 : ASSERT(SUCCESS == db_read_stat_blob_by_row_id(rollback_db_filename,
766 : : row_id,
767 : : after_blob,
768 : : sizeof(after_blob),
769 : : &after_blob_size));
770 : :
771 : 2 : ASSERT(before_blob_size == after_blob_size);
772 : :
773 [ + - ]: 2 : if(before_blob_size > 0)
774 : : {
775 : 2 : ASSERT(0 == memcmp(before_blob,after_blob,(size_t)before_blob_size));
776 : : }
777 : :
778 : 2 : int v1_rows_after = 0;
779 : 2 : ASSERT(SUCCESS == db_read_files_count_with_blob_size(rollback_db_filename,
780 : : (int)sizeof(CmpctStat_v1),
781 : : &v1_rows_after));
782 : 2 : ASSERT(v1_rows_before == v1_rows_after);
783 : :
784 : 2 : ASSERT(SUCCESS == delete_path(rollback_db_filename));
785 : 2 : ASSERT(SUCCESS == delete_path(reference_db_filename));
786 : :
787 : 2 : m_del(result);
788 : 2 : m_del(pattern);
789 : :
790 : 2 : RETURN_STATUS;
791 : : }
792 : :
793 : : /**
794 : : * @brief Warn when metadata declares a DB version newer than supported
795 : : *
796 : : * Verify both testing and non-testing modes keep the future DB unchanged
797 : : */
798 : 2 : Return test0015_19(void)
799 : : {
800 : 2 : INITTEST;
801 : :
802 : 2 : ASSERT(SUCCESS == set_environment_variable("TESTING","true"));
803 : :
804 : 2 : const char *db_filename = "0015_database_future_version.db";
805 : 2 : const char *arguments = "--database=0015_database_future_version.db "
806 : : "tests/fixtures/diffs/diff1";
807 : :
808 : 2 : m_create(char,result,MEMORY_STRING);
809 : 2 : m_create(char,pattern,MEMORY_STRING);
810 : :
811 : 2 : ASSERT(SUCCESS == runit(arguments,NULL,NULL,COMPLETED,ALLOW_BOTH));
812 : 2 : ASSERT(SUCCESS == set_db_version_in_metadata(db_filename,CURRENT_DB_VERSION + 1));
813 : :
814 : 2 : int db_version = 0;
815 : 2 : ASSERT(SUCCESS == read_db_version_from_metadata(db_filename,&db_version));
816 : 2 : ASSERT(db_version == CURRENT_DB_VERSION + 1);
817 : :
818 : 2 : ASSERT(SUCCESS == runit(arguments,result,NULL,WARNING,ALLOW_BOTH));
819 : :
820 : 2 : const char *filename = "templates/0015_018_1.txt";
821 : :
822 : 2 : ASSERT(SUCCESS == get_file_content(filename,pattern));
823 : 2 : ASSERT(SUCCESS == match_pattern(result,pattern,filename));
824 : :
825 : 2 : ASSERT(SUCCESS == read_db_version_from_metadata(db_filename,&db_version));
826 : 2 : ASSERT(db_version == CURRENT_DB_VERSION + 1);
827 : :
828 : 2 : ASSERT(SUCCESS == set_environment_variable("TESTING","false"));
829 : 2 : ASSERT(SUCCESS == runit(arguments,result,NULL,WARNING,ALLOW_BOTH));
830 : :
831 : 2 : filename = "templates/0015_018_2.txt";
832 : :
833 : 2 : ASSERT(SUCCESS == get_file_content(filename,pattern));
834 : 2 : ASSERT(SUCCESS == match_pattern(result,pattern,filename));
835 : :
836 : 2 : ASSERT(SUCCESS == delete_path(db_filename));
837 : :
838 : 2 : m_del(result);
839 : 2 : m_del(pattern);
840 : :
841 : 2 : RETURN_STATUS;
842 : : }
843 : :
844 : : /**
845 : : * @brief Exercise DB upgrade and migration regressions across legacy formats
846 : : *
847 : : * This scenario covers primary DB upgrades from legacy versions 0 through 3,
848 : : * comparison-driven upgrades, default DB creation, UTF-8 filename handling,
849 : : * checksum compatibility against a legacy v4 reference DB, resilient handling
850 : : * of corrupted legacy stat blobs, rollback on forced SQLite migration failure,
851 : : * and warning behavior for a DB that reports a future metadata version
852 : : */
853 : 2 : Return test0015(void)
854 : : {
855 : 2 : INITTEST;
856 : :
857 : 2 : TEST(test0015_1,"Upgrade a DB from v0 to the current version. Error handling");
858 : 2 : TEST(test0015_2,"Upgrade a DB from v0 to the current version as the primary database");
859 : 2 : TEST(test0015_3,"Upgrade a DB from v0 to the current version with --watch-timestamps");
860 : 2 : TEST(test0015_4,"Verify that the upgraded v0 DB is actually at the current version");
861 : 2 : TEST(test0015_5,"Create default name database");
862 : 2 : TEST(test0015_6,"Attempting an upgrade with a single --compare parameter");
863 : 2 : TEST(test0015_7,"Upgrading from 0 to the last version using the --compare and --update");
864 : 2 : TEST(test0015_8,"Upgrade a DB from v1 to the current version as the primary database");
865 : 2 : TEST(test0015_9,"Verify that the upgraded v1 DB is actually at the current version");
866 : 2 : TEST(test0015_10,"Upgrading from 1 to the last version using the --compare and --update");
867 : 2 : TEST(test0015_11,"Upgrade a DB from v2 to the current version as the primary database");
868 : 2 : TEST(test0015_12,"Upgrading from 2 to the last version using the --compare and --update");
869 : 2 : TEST(test0015_13,"Upgrading from 3 with UTF-8 name to the last version using the --update");
870 : 2 : TEST(test0015_14,"Upgrading from 3 to the last version using the --compare and --update");
871 : 2 : TEST(test0015_15,"Create and compare DBs with UTF-8 names and checksums from legacy DB");
872 : 2 : TEST(test0015_16,"Corrupted v0 stat blob does not break the full upgrade");
873 : 2 : TEST(test0015_17,"Corrupted v3 stat blob does not break the full upgrade");
874 : 2 : TEST(test0015_18,"Forced SQLite failure triggers rollback during 3->4 migration");
875 : 2 : TEST(test0015_19,"Fresh DB with forced future version returns warning and stays unchanged");
876 : :
877 : 2 : RETURN_STATUS;
878 : : }
|