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 CLI and configuration runs select QUICK verification and print the
100 : : * same upgrade warning. The rejected upgrade leaves the fixture unchanged
101 : : */
102 : 2 : Return test0015_1(void)
103 : : {
104 : 2 : INITTEST;
105 : :
106 : 2 : ASSERT(SUCCESS == set_environment_variable("TESTING","true"));
107 : 2 : ASSERT(SUCCESS == copy_path("tests/templates/0015_database_v0.db","0015_database_v0.db"));
108 : :
109 : 2 : const char *arguments = "--check-level=QUICK --database=./0015_database_v0.db tests/fixtures/diffs/diff1";
110 : 2 : const char *filename = "templates/0015_001.txt";
111 : 2 : const struct argument_test_case cases[] = {
112 : : {arguments,WARNING,filename,STDERR_MUST_BE_EMPTY},
113 : : {0}
114 : : };
115 : :
116 : : // Match the result against the pattern
117 : 2 : ASSERT(SUCCESS == run_and_match_templates(cases,NULL,NULL));
118 : :
119 : : /* The rejected upgrade leaves the same database ready for the configuration run */
120 : 2 : ASSERT(SUCCESS == copy_path("tests/templates/0015_001.conf","0015_001.conf"));
121 : 2 : const struct argument_test_case file_cases[] = {
122 : : {"--config=0015_001.conf",WARNING,filename,STDERR_MUST_BE_EMPTY},
123 : : {0}
124 : : };
125 : 2 : ASSERT(SUCCESS == run_and_match_templates(file_cases,NULL,NULL));
126 : 2 : call(delete_path_if_present("0015_001.conf"));
127 : :
128 : : // Clean up test results
129 : 2 : ASSERT(SUCCESS == delete_path("0015_database_v0.db"));
130 : :
131 : 2 : RETURN_STATUS;
132 : : }
133 : :
134 : : /**
135 : : * @brief Upgrade a legacy v0 DB as the primary database with --update
136 : : *
137 : : * Verify the upgrade succeeds and produces the expected output
138 : : */
139 : 2 : Return test0015_2(void)
140 : : {
141 : 2 : INITTEST;
142 : 2 : ASSERT(SUCCESS == copy_path("tests/templates/0015_database_v0.db","0015_database_v0.db"));
143 : :
144 : 2 : ASSERT(SUCCESS == set_environment_variable("TESTING","true"));
145 : :
146 : 2 : const char *arguments = "--check-level=QUICK --update --database=0015_database_v0.db "
147 : : "tests/fixtures/diffs/diff1";
148 : :
149 : 2 : m_create(char,result,MEMORY_STRING);
150 : 2 : m_create(char,pattern,MEMORY_STRING);
151 : :
152 : 2 : const char *filename = "templates/0015_002_1.txt";
153 : :
154 : 2 : ASSERT(SUCCESS == runit(arguments,result,NULL,COMPLETED,ALLOW_BOTH));
155 : :
156 : 2 : ASSERT(SUCCESS == get_file_content(filename,pattern));
157 : :
158 : : // Match the result against the pattern
159 : 2 : ASSERT(SUCCESS == match_pattern(result,pattern,filename));
160 : :
161 : : // Clean up test results
162 : 2 : ASSERT(SUCCESS == delete_path("0015_database_v0.db"));
163 : :
164 : : // Clean to use it iteratively
165 : 2 : m_del(pattern);
166 : 2 : m_del(result);
167 : :
168 : 2 : RETURN_STATUS;
169 : : }
170 : :
171 : : /**
172 : : * @brief Upgrade a legacy v0 DB with --watch-timestamps enabled
173 : : *
174 : : * Verify the upgrade succeeds and reports timestamp details in the output
175 : : */
176 : 2 : Return test0015_3(void)
177 : : {
178 : 2 : INITTEST;
179 : 2 : ASSERT(SUCCESS == copy_path("tests/templates/0015_database_v0.db","0015_database_v0.db"));
180 : :
181 : 2 : ASSERT(SUCCESS == set_environment_variable("TESTING","true"));
182 : :
183 : 2 : const char *arguments = "--check-level=QUICK --watch-timestamps --update --database=0015_database_v0.db "
184 : : "tests/fixtures/diffs/diff1";
185 : :
186 : 2 : m_create(char,result,MEMORY_STRING);
187 : 2 : m_create(char,pattern,MEMORY_STRING);
188 : :
189 : 2 : const char *filename = "templates/0015_002_2.txt";
190 : :
191 : 2 : ASSERT(SUCCESS == runit(arguments,result,NULL,COMPLETED,ALLOW_BOTH));
192 : :
193 : 2 : ASSERT(SUCCESS == get_file_content(filename,pattern));
194 : :
195 : : // Match the result against the pattern
196 : 2 : ASSERT(SUCCESS == match_pattern(result,pattern,filename));
197 : :
198 : : // Clean to use it iteratively
199 : 2 : m_del(pattern);
200 : 2 : m_del(result);
201 : :
202 : 2 : RETURN_STATUS;
203 : : }
204 : :
205 : : /**
206 : : * @brief Re-run upgrade for the already upgraded v0 DB
207 : : *
208 : : * Verify the database passes the default full integrity check, is treated
209 : : * as current, and the run stays successful
210 : : */
211 : 2 : Return test0015_4(void)
212 : : {
213 : 2 : INITTEST;
214 : :
215 : 2 : m_create(char,result,MEMORY_STRING);
216 : 2 : m_create(char,pattern,MEMORY_STRING);
217 : :
218 : 2 : const char *filename = "templates/0015_003.txt";
219 : :
220 : 2 : ASSERT(SUCCESS == set_environment_variable("TESTING","true"));
221 : :
222 : 2 : const char *arguments = "--update --database=./0015_database_v0.db "
223 : : "tests/fixtures/diffs/diff1";
224 : :
225 : 2 : ASSERT(SUCCESS == runit(arguments,result,NULL,COMPLETED,ALLOW_BOTH));
226 : :
227 : 2 : ASSERT(SUCCESS == get_file_content(filename,pattern));
228 : :
229 : : // Match the result against the pattern
230 : 2 : ASSERT(SUCCESS == match_pattern(result,pattern,filename));
231 : :
232 : : // Clean to use it iteratively
233 : 2 : m_del(pattern);
234 : 2 : m_del(result);
235 : :
236 : : // Clean up test results
237 : 2 : ASSERT(SUCCESS == delete_path("0015_database_v0.db"));
238 : :
239 : 2 : RETURN_STATUS;
240 : : }
241 : :
242 : : /**
243 : : * @brief Create a fresh DB with the default generated filename
244 : : *
245 : : * Verify CLI and file-supplied roots produce the same generated DB name and
246 : : * match the same complete output template. The template allows the additional
247 : : * configuration-path diagnostic for the file run
248 : : */
249 : 2 : Return test0015_5(void)
250 : : {
251 : 2 : INITTEST;
252 : :
253 : 2 : ASSERT(SUCCESS == set_environment_variable("TESTING","true"));
254 : :
255 : : // Run the application without an explicit DB name
256 : 2 : const char *arguments = "tests/fixtures/diffs/diff1";
257 : :
258 : 2 : const char *filename = "templates/0015_004.txt"; // File name
259 : 2 : const char *template = "%DB_NAME%";
260 : :
261 : 2 : const char *replacement = getenv("DBNAME"); // Database name
262 : :
263 : 2 : ASSERT(replacement != NULL);
264 : :
265 : 2 : const struct argument_test_case cases[] = {
266 : : {arguments,COMPLETED,filename,STDERR_MUST_BE_EMPTY},
267 : : {0}
268 : : };
269 : :
270 : 2 : ASSERT(SUCCESS == run_and_match_templates(cases,template,replacement));
271 : :
272 : : /* Repeat the target creation from the same initial state. The resulting
273 : : database remains available to the following comparison tests */
274 : 2 : ASSERT(SUCCESS == delete_path(replacement));
275 : 2 : ASSERT(SUCCESS == copy_from_origin("tests/templates/0015_004.conf",
276 : : "tests/templates/0015_004.conf",REQUIRE_SOURCE_EXISTS));
277 : 2 : const struct argument_test_case file_cases[] = {
278 : : {"--config=tests/templates/0015_004.conf",COMPLETED,filename,STDERR_MUST_BE_EMPTY},
279 : : {0}
280 : : };
281 : 2 : ASSERT(SUCCESS == run_and_match_templates(file_cases,template,replacement));
282 : 2 : call(delete_path_if_present("tests/templates/0015_004.conf"));
283 : :
284 : 2 : RETURN_STATUS;
285 : : }
286 : :
287 : : /**
288 : : * @brief Compare a current DB against a legacy v0 DB without --update
289 : : *
290 : : * Verify the application warns that the legacy DB must be upgraded first
291 : : */
292 : 2 : Return test0015_6(void)
293 : : {
294 : 2 : INITTEST;
295 : :
296 : 2 : ASSERT(SUCCESS == set_environment_variable("TESTING","true"));
297 : :
298 : 2 : ASSERT(SUCCESS == copy_path("tests/templates/0015_database_v0.db","0015_database_v0.db"));
299 : :
300 : 2 : const char *arguments = "--check-level=QUICK --compare $DBNAME 0015_database_v0.db";
301 : :
302 : 2 : const char *filename = "templates/0015_005.txt"; // File name
303 : 2 : const char *template = "%DB_NAME%";
304 : :
305 : 2 : const char *replacement = getenv("DBNAME"); // Database name
306 : :
307 : 2 : ASSERT(replacement != NULL);
308 : :
309 : 2 : const struct argument_test_case cases[] = {
310 : : {arguments,WARNING,filename,STDERR_MUST_BE_EMPTY},
311 : : {0}
312 : : };
313 : :
314 : 2 : ASSERT(SUCCESS == run_and_match_templates(cases,template,replacement));
315 : :
316 : 2 : RETURN_STATUS;
317 : : }
318 : :
319 : : /**
320 : : * @brief Compare and upgrade a legacy v0 DB with --compare --update
321 : : *
322 : : * Verify the legacy DB is upgraded during comparison and the run completes
323 : : */
324 : 2 : Return test0015_7(void)
325 : : {
326 : 2 : INITTEST;
327 : :
328 : 2 : ASSERT(SUCCESS == set_environment_variable("TESTING","true"));
329 : 2 : ASSERT(SUCCESS == copy_path("0015_database_v0.db","0015_006.db"));
330 : :
331 : : // Run the comparison and upgrade flow
332 : 2 : const char *arguments = "--check-level=QUICK --compare --update $DBNAME 0015_database_v0.db";
333 : :
334 : 2 : const char *filename = "templates/0015_006.txt"; // File name
335 : 2 : const char *template = "%DB_NAME%";
336 : :
337 : 2 : const char *replacement = getenv("DBNAME"); // Database name
338 : :
339 : 2 : ASSERT(replacement != NULL);
340 : :
341 : 2 : const struct argument_test_case cases[] = {
342 : : {arguments,COMPLETED,filename,STDERR_MUST_BE_EMPTY},
343 : : {0}
344 : : };
345 : :
346 : 2 : ASSERT(SUCCESS == run_and_match_templates(cases,template,replacement));
347 : :
348 : : /* Restore the legacy database so the file run performs the same upgrade */
349 : 2 : ASSERT(SUCCESS == copy_path("0015_006.db","0015_database_v0.db"));
350 : 2 : ASSERT(SUCCESS == copy_path("tests/templates/0015_006.conf","0015_006.conf"));
351 : 2 : ASSERT(SUCCESS == replace_placeholder_in_file("$TMPDIR/0015_006.conf","%DB_NAME%","$DBNAME"));
352 : 2 : const struct argument_test_case file_cases[] = {
353 : : {"--config=0015_006.conf",COMPLETED,filename,STDERR_MUST_BE_EMPTY},
354 : : {0}
355 : : };
356 : 2 : ASSERT(SUCCESS == run_and_match_templates(file_cases,template,replacement));
357 : 2 : call(delete_path_if_present("0015_006.conf"));
358 : 2 : call(delete_path_if_present("0015_006.db"));
359 : :
360 : : // Clean up test results
361 : 2 : ASSERT(SUCCESS == delete_path("0015_database_v0.db"));
362 : :
363 : 2 : RETURN_STATUS;
364 : : }
365 : :
366 : : /**
367 : : * @brief Upgrade a legacy v1 DB as the primary database with --update
368 : : *
369 : : * Verify the upgrade succeeds and produces the expected output
370 : : */
371 : 2 : Return test0015_8(void)
372 : : {
373 : 2 : INITTEST;
374 : :
375 : 2 : ASSERT(SUCCESS == set_environment_variable("TESTING","true"));
376 : 2 : ASSERT(SUCCESS == copy_path("tests/templates/0015_database_v1.db","0015_database_v1.db"));
377 : :
378 : 2 : const char *arguments = "--check-level=QUICK --update --database=0015_database_v1.db "
379 : : "tests/fixtures/diffs/diff1";
380 : :
381 : 2 : m_create(char,result,MEMORY_STRING);
382 : 2 : m_create(char,pattern,MEMORY_STRING);
383 : :
384 : 2 : const char *filename = "templates/0015_007.txt";
385 : :
386 : 2 : ASSERT(SUCCESS == runit(arguments,result,NULL,COMPLETED,ALLOW_BOTH));
387 : :
388 : 2 : ASSERT(SUCCESS == get_file_content(filename,pattern));
389 : :
390 : : // Match the result against the pattern
391 : 2 : ASSERT(SUCCESS == match_pattern(result,pattern,filename));
392 : :
393 : : // Clean to use it iteratively
394 : 2 : m_del(pattern);
395 : 2 : m_del(result);
396 : :
397 : 2 : RETURN_STATUS;
398 : : }
399 : :
400 : : /**
401 : : * @brief Re-run upgrade for the already upgraded v1 DB
402 : : *
403 : : * Verify the database passes the default full integrity check, is treated
404 : : * as current, and the run stays successful
405 : : */
406 : 2 : Return test0015_9(void)
407 : : {
408 : 2 : INITTEST;
409 : :
410 : 2 : m_create(char,result,MEMORY_STRING);
411 : 2 : m_create(char,pattern,MEMORY_STRING);
412 : :
413 : 2 : const char *filename = "templates/0015_008.txt";
414 : :
415 : 2 : ASSERT(SUCCESS == set_environment_variable("TESTING","true"));
416 : :
417 : 2 : const char *arguments = "--update --database=./0015_database_v1.db "
418 : : "tests/fixtures/diffs/diff1";
419 : :
420 : 2 : ASSERT(SUCCESS == runit(arguments,result,NULL,COMPLETED,ALLOW_BOTH));
421 : :
422 : 2 : ASSERT(SUCCESS == get_file_content(filename,pattern));
423 : :
424 : : // Match the result against the pattern
425 : 2 : ASSERT(SUCCESS == match_pattern(result,pattern,filename));
426 : :
427 : : // Clean to use it iteratively
428 : 2 : m_del(pattern);
429 : 2 : m_del(result);
430 : :
431 : : // Clean up test results
432 : 2 : ASSERT(SUCCESS == delete_path("0015_database_v1.db"));
433 : :
434 : 2 : RETURN_STATUS;
435 : : }
436 : :
437 : : /**
438 : : * @brief Compare and upgrade a legacy v1 DB with --compare --update
439 : : *
440 : : * Verify the legacy DB is upgraded during comparison and the run completes
441 : : */
442 : 2 : Return test0015_10(void)
443 : : {
444 : 2 : INITTEST;
445 : :
446 : 2 : ASSERT(SUCCESS == set_environment_variable("TESTING","true"));
447 : :
448 : : // Run the comparison and upgrade flow
449 : 2 : ASSERT(SUCCESS == copy_path("tests/templates/0015_database_v1.db","0015_database_v1.db"));
450 : :
451 : 2 : const char *arguments = "--check-level=QUICK --compare --update $DBNAME 0015_database_v1.db";
452 : :
453 : 2 : const char *filename = "templates/0015_009.txt"; // File name
454 : 2 : const char *template = "%DB_NAME%";
455 : :
456 : 2 : const char *replacement = getenv("DBNAME"); // Database name
457 : :
458 : 2 : ASSERT(replacement != NULL);
459 : :
460 : 2 : const struct argument_test_case cases[] = {
461 : : {arguments,COMPLETED,filename,STDERR_MUST_BE_EMPTY},
462 : : {0}
463 : : };
464 : :
465 : 2 : ASSERT(SUCCESS == run_and_match_templates(cases,template,replacement));
466 : :
467 : : // Clean up test results
468 : 2 : ASSERT(SUCCESS == delete_path("0015_database_v1.db"));
469 : :
470 : 2 : RETURN_STATUS;
471 : : }
472 : :
473 : : /**
474 : : * @brief Upgrade a legacy v2 DB as the primary database with --update
475 : : *
476 : : * Verify the upgrade succeeds and prints the verbose migration details
477 : : */
478 : 2 : Return test0015_11(void)
479 : : {
480 : 2 : INITTEST;
481 : :
482 : 2 : ASSERT(SUCCESS == set_environment_variable("TESTING","false"));
483 : 2 : ASSERT(SUCCESS == copy_path("tests/templates/0015_database_v2.db","0015_database_v2.db"));
484 : :
485 : 2 : const char *arguments = "--check-level=QUICK --update --database=0015_database_v2.db --verbose "
486 : : "tests/fixtures/diffs/diff1";
487 : :
488 : 2 : m_create(char,result,MEMORY_STRING);
489 : 2 : m_create(char,pattern,MEMORY_STRING);
490 : :
491 : 2 : ASSERT(SUCCESS == runit(arguments,result,NULL,COMPLETED,ALLOW_BOTH));
492 : :
493 : 2 : const char *filename = "templates/0015_010.txt";
494 : :
495 : 2 : ASSERT(SUCCESS == get_file_content(filename,pattern));
496 : :
497 : : // Match the result against the pattern
498 : 2 : ASSERT(SUCCESS == match_pattern(result,pattern,filename));
499 : :
500 : : // Clean up test results
501 : 2 : ASSERT(SUCCESS == delete_path("0015_database_v2.db"));
502 : :
503 : : // Clean to use it iteratively
504 : 2 : m_del(pattern);
505 : 2 : m_del(result);
506 : :
507 : 2 : RETURN_STATUS;
508 : : }
509 : :
510 : : /**
511 : : * @brief Compare and upgrade a legacy v2 DB with --compare --update
512 : : *
513 : : * Verify the legacy DB is upgraded during comparison and the run completes
514 : : */
515 : 2 : Return test0015_12(void)
516 : : {
517 : 2 : INITTEST;
518 : :
519 : 2 : ASSERT(SUCCESS == set_environment_variable("TESTING","true"));
520 : :
521 : : // Run the comparison and upgrade flow
522 : 2 : ASSERT(SUCCESS == copy_path("tests/templates/0015_database_v2.db","0015_database_v2.db"));
523 : :
524 : 2 : const char *arguments = "--check-level=QUICK --compare --update $DBNAME 0015_database_v2.db";
525 : :
526 : 2 : const char *filename = "templates/0015_011.txt"; // File name
527 : 2 : const char *template = "%DB_NAME%";
528 : :
529 : 2 : const char *replacement = getenv("DBNAME"); // Database name
530 : :
531 : 2 : ASSERT(replacement != NULL);
532 : :
533 : 2 : const struct argument_test_case cases[] = {
534 : : {arguments,COMPLETED,filename,STDERR_MUST_BE_EMPTY},
535 : : {0}
536 : : };
537 : :
538 : 2 : ASSERT(SUCCESS == run_and_match_templates(cases,template,replacement));
539 : :
540 : : // Clean up test results
541 : 2 : ASSERT(SUCCESS == delete_path(replacement));
542 : 2 : ASSERT(SUCCESS == delete_path("0015_database_v2.db"));
543 : :
544 : 2 : RETURN_STATUS;
545 : : }
546 : :
547 : : /**
548 : : * @brief Upgrade a legacy v3 DB whose filename contains UTF-8 characters
549 : : *
550 : : * Verify the primary DB upgrade works with spaces and non-ASCII characters
551 : : * through CLI and configuration. Both runs start from the same v3 fixture
552 : : * and use the same full output template
553 : : *
554 : : * @return Return status for the CLI and configuration upgrade checks
555 : : */
556 : 2 : Return test0015_13(void)
557 : : {
558 : 2 : INITTEST;
559 : :
560 : 2 : ASSERT(SUCCESS == set_environment_variable("TESTING","true"));
561 : :
562 : 2 : ASSERT(SUCCESS == copy_path("tests/templates/0015_database_v3 это база данных с пробелами и символами UTF-8.db","0015_database_v3 это база данных с пробелами и символами UTF-8.db"));
563 : :
564 : 2 : const char *arguments = "--check-level=QUICK --update --database=\"0015_database_v3 это база данных с пробелами и символами UTF-8.db\" "
565 : : "tests/fixtures/diffs/diff1";
566 : 2 : const char *filename = "templates/0015_012.txt";
567 : 2 : const struct argument_test_case cases[] = {
568 : : {arguments,COMPLETED,filename,STDERR_MUST_BE_EMPTY},
569 : : {0}
570 : : };
571 : :
572 : : // Match the result against the pattern
573 : 2 : ASSERT(SUCCESS == run_and_match_templates(cases,NULL,NULL));
574 : :
575 : : /* Restore the original v3 database before repeating the upgrade through configuration */
576 : 2 : ASSERT(SUCCESS == copy_path("tests/templates/0015_database_v3 это база данных с пробелами и символами UTF-8.db","0015_database_v3 это база данных с пробелами и символами UTF-8.db"));
577 : 2 : ASSERT(SUCCESS == copy_path("tests/templates/0015_013.conf","0015_013.conf"));
578 : 2 : const struct argument_test_case file_cases[] = {
579 : : {"--config=0015_013.conf",COMPLETED,filename,STDERR_MUST_BE_EMPTY},
580 : : {0}
581 : : };
582 : 2 : ASSERT(SUCCESS == run_and_match_templates(file_cases,NULL,NULL));
583 : :
584 : : // Clean up test results
585 : 2 : call(delete_path_if_present("0015_013.conf"));
586 : 2 : ASSERT(SUCCESS == delete_path("0015_database_v3 это база данных с пробелами и символами UTF-8.db"));
587 : :
588 : 2 : RETURN_STATUS;
589 : : }
590 : :
591 : : /**
592 : : * @brief Compare and upgrade a legacy v3 UTF-8 DB with --compare --update
593 : : *
594 : : * Verify the upgrade path works when both compared DB filenames contain UTF-8
595 : : */
596 : 2 : Return test0015_14(void)
597 : : {
598 : 2 : INITTEST;
599 : :
600 : 2 : ASSERT(SUCCESS == set_environment_variable("TESTING","true"));
601 : :
602 : 2 : ASSERT(SUCCESS == copy_path("tests/templates/0015_database_v3 это база данных с пробелами и символами UTF-8.db","0015_database_v3 это база данных с пробелами и символами UTF-8.db"));
603 : :
604 : 2 : ASSERT(SUCCESS == copy_path("tests/templates/0015_database_v4 это база данных с пробелами и символами UTF-8.db","0015_database_v4 это база данных с пробелами и символами UTF-8.db"));
605 : :
606 : 2 : const char *arguments = "--check-level=QUICK --compare --update "
607 : : "\"0015_database_v3 это база данных с пробелами и символами UTF-8.db\" "
608 : : "\"0015_database_v4 это база данных с пробелами и символами UTF-8.db\"";
609 : :
610 : 2 : m_create(char,result,MEMORY_STRING);
611 : 2 : m_create(char,pattern,MEMORY_STRING);
612 : :
613 : 2 : ASSERT(SUCCESS == runit(arguments,result,NULL,COMPLETED,ALLOW_BOTH));
614 : :
615 : 2 : const char *filename = "templates/0015_013.txt";
616 : :
617 : 2 : ASSERT(SUCCESS == get_file_content(filename,pattern));
618 : :
619 : : // Match the result against the pattern
620 : 2 : ASSERT(SUCCESS == match_pattern(result,pattern,filename));
621 : :
622 : 2 : m_del(result);
623 : 2 : m_del(pattern);
624 : :
625 : 2 : ASSERT(SUCCESS == delete_path("0015_database_v3 это база данных с пробелами и символами UTF-8.db"));
626 : 2 : ASSERT(SUCCESS == delete_path("0015_database_v4 это база данных с пробелами и символами UTF-8.db"));
627 : :
628 : 2 : RETURN_STATUS;
629 : : }
630 : :
631 : : /**
632 : : * @brief Compare a fresh UTF-8 DB against a legacy UTF-8 v4 reference DB
633 : : *
634 : : * Verify the application can create, read, and compare DB filenames with
635 : : * spaces and non-ASCII characters while keeping checksum compatibility
636 : : */
637 : 2 : Return test0015_15(void)
638 : : {
639 : 2 : INITTEST;
640 : :
641 : 2 : ASSERT(SUCCESS == set_environment_variable("TESTING","true"));
642 : :
643 : 2 : ASSERT(SUCCESS == copy_path("tests/templates/0015_database_v4 это база данных с пробелами и символами UTF-8.db","0015_database_v4 это база данных с пробелами и символами UTF-8.db"));
644 : :
645 : 2 : m_create(char,pattern,MEMORY_STRING);
646 : 2 : m_create(char,result,MEMORY_STRING);
647 : 2 : m_create(char,chunk,MEMORY_STRING);
648 : :
649 : 2 : const char *arguments = "--database=\"Это новая база данных.db\" "
650 : : "tests/fixtures/diffs/diff1";
651 : :
652 : 2 : ASSERT(SUCCESS == runit(arguments,chunk,NULL,COMPLETED,ALLOW_BOTH));
653 : 2 : ASSERT(SUCCESS == m_copy(result,chunk));
654 : :
655 : 2 : arguments = "--check-level=QUICK --compare \"Это новая база данных.db\" "
656 : : "\"0015_database_v4 это база данных с пробелами и символами UTF-8.db\"";
657 : :
658 : 2 : ASSERT(SUCCESS == runit(arguments,chunk,NULL,COMPLETED,ALLOW_BOTH));
659 : 2 : ASSERT(SUCCESS == m_concat_strings(result,chunk));
660 : :
661 : 2 : const char *filename = "templates/0015_014.txt";
662 : :
663 : 2 : ASSERT(SUCCESS == get_file_content(filename,pattern));
664 : :
665 : : // Match the result against the pattern
666 : 2 : ASSERT(SUCCESS == match_pattern(result,pattern,filename));
667 : :
668 : : // Clean to use it iteratively
669 : 2 : m_del(pattern);
670 : 2 : m_del(result);
671 : 2 : m_del(chunk);
672 : :
673 : : // Clean up test results
674 : 2 : ASSERT(SUCCESS == delete_path("Это новая база данных.db"));
675 : 2 : ASSERT(SUCCESS == delete_path("0015_database_v4 это база данных с пробелами и символами UTF-8.db"));
676 : :
677 : 2 : RETURN_STATUS;
678 : : }
679 : :
680 : : /**
681 : : * @brief Upgrade a v0 DB with one corrupted legacy stat blob
682 : : *
683 : : * Verify the full upgrade still completes and the corrupted row is converted
684 : : * into the expected zero-source compact stat representation
685 : : */
686 : 2 : Return test0015_16(void)
687 : : {
688 : 2 : INITTEST;
689 : 2 : const char *corrupted_db_filename = "0015_database_v0_corrupt.db";
690 : 2 : const char *reference_db_filename = "0015_database_v4_reference.db";
691 : :
692 : 2 : ASSERT(SUCCESS == set_environment_variable("TESTING","true"));
693 : 2 : ASSERT(SUCCESS == copy_path("tests/templates/0015_database_v0.db",corrupted_db_filename));
694 : 2 : ASSERT(SUCCESS == copy_path("tests/templates/0015_database_v4 это база данных с пробелами и символами UTF-8.db",reference_db_filename));
695 : :
696 : 2 : sqlite3_int64 row_id = 0;
697 : :
698 : 2 : ASSERT(SUCCESS == db_corrupt_first_row_stat_blob(corrupted_db_filename,&row_id));
699 : :
700 : 2 : m_create(char,result,MEMORY_STRING);
701 : 2 : m_create(char,pattern,MEMORY_STRING);
702 : :
703 : 2 : const char *arguments = "--check-level=QUICK --compare --update 0015_database_v4_reference.db 0015_database_v0_corrupt.db";
704 : :
705 : 2 : ASSERT(SUCCESS == runit(arguments,result,NULL,COMPLETED,ALLOW_BOTH));
706 : 2 : const char *filename = "templates/0015_015.txt";
707 : 2 : ASSERT(SUCCESS == get_file_content(filename,pattern));
708 : 2 : ASSERT(SUCCESS == match_pattern(result,pattern,filename));
709 : :
710 : 2 : int db_version = 0;
711 : 2 : ASSERT(SUCCESS == read_db_version_from_metadata(corrupted_db_filename,&db_version));
712 : 2 : ASSERT(db_version == 4);
713 : :
714 : 2 : CmpctStat stat = {0};
715 : 2 : ASSERT(SUCCESS == db_read_cmpctstat_by_row_id(corrupted_db_filename,row_id,&stat));
716 : 2 : ASSERT(SUCCESS == db_verify_zero_converted_cmpctstat(&stat));
717 : :
718 : 2 : ASSERT(SUCCESS == delete_path(corrupted_db_filename));
719 : 2 : ASSERT(SUCCESS == delete_path(reference_db_filename));
720 : :
721 : 2 : m_del(result);
722 : 2 : m_del(pattern);
723 : :
724 : 2 : RETURN_STATUS;
725 : : }
726 : :
727 : : /**
728 : : * @brief Upgrade a v3 DB with one corrupted legacy stat blob
729 : : *
730 : : * Verify the full upgrade still completes and the corrupted row is stored
731 : : * using the zero-source conversion logic
732 : : */
733 : 2 : Return test0015_17(void)
734 : : {
735 : 2 : INITTEST;
736 : 2 : const char *corrupted_db_filename = "0015_database_v3_corrupt.db";
737 : 2 : const char *reference_db_filename = "0015_database_v4_reference.db";
738 : :
739 : 2 : ASSERT(SUCCESS == set_environment_variable("TESTING","true"));
740 : 2 : ASSERT(SUCCESS == copy_path("tests/templates/0015_database_v3 это база данных с пробелами и символами UTF-8.db",corrupted_db_filename));
741 : 2 : ASSERT(SUCCESS == copy_path("tests/templates/0015_database_v4 это база данных с пробелами и символами UTF-8.db",reference_db_filename));
742 : :
743 : 2 : sqlite3_int64 row_id = 0;
744 : 2 : ASSERT(SUCCESS == db_corrupt_first_row_stat_blob(corrupted_db_filename,&row_id));
745 : :
746 : 2 : const char *arguments = "--check-level=QUICK --compare --update 0015_database_v4_reference.db 0015_database_v3_corrupt.db";
747 : :
748 : 2 : m_create(char,result,MEMORY_STRING);
749 : 2 : m_create(char,pattern,MEMORY_STRING);
750 : :
751 : 2 : ASSERT(SUCCESS == runit(arguments,result,NULL,COMPLETED,ALLOW_BOTH));
752 : 2 : const char *filename = "templates/0015_016.txt";
753 : 2 : ASSERT(SUCCESS == get_file_content(filename,pattern));
754 : 2 : ASSERT(SUCCESS == match_pattern(result,pattern,filename));
755 : :
756 : 2 : int db_version = 0;
757 : 2 : ASSERT(SUCCESS == read_db_version_from_metadata(corrupted_db_filename,&db_version));
758 : 2 : ASSERT(db_version == 4);
759 : :
760 : 2 : CmpctStat stat = {0};
761 : 2 : ASSERT(SUCCESS == db_read_cmpctstat_by_row_id(corrupted_db_filename,row_id,&stat));
762 : 2 : ASSERT(SUCCESS == db_verify_zero_converted_cmpctstat(&stat));
763 : :
764 : 2 : ASSERT(SUCCESS == delete_path(corrupted_db_filename));
765 : 2 : ASSERT(SUCCESS == delete_path(reference_db_filename));
766 : :
767 : 2 : m_del(result);
768 : 2 : m_del(pattern);
769 : :
770 : 2 : RETURN_STATUS;
771 : : }
772 : :
773 : : /**
774 : : * @brief Roll back 3->4 migration when SQLite fails during stat conversion
775 : : *
776 : : * Verify the transaction is rolled back and the DB content stays unchanged
777 : : */
778 : 2 : Return test0015_18(void)
779 : : {
780 : 2 : INITTEST;
781 : 2 : const char *rollback_db_filename = "0015_database_v3_rollback.db";
782 : 2 : const char *reference_db_filename = "0015_database_v4_reference.db";
783 : :
784 : 2 : ASSERT(SUCCESS == set_environment_variable("TESTING","true"));
785 : 2 : ASSERT(SUCCESS == copy_path("tests/templates/0015_database_v3 это база данных с пробелами и символами UTF-8.db",rollback_db_filename));
786 : 2 : ASSERT(SUCCESS == copy_path("tests/templates/0015_database_v4 это база данных с пробелами и символами UTF-8.db",reference_db_filename));
787 : :
788 : 2 : int files_count = 0;
789 : 2 : ASSERT(SUCCESS == db_read_files_count(rollback_db_filename,&files_count));
790 : 2 : ASSERT(files_count >= 2);
791 : :
792 : 2 : sqlite3_int64 row_id = 0;
793 : 2 : ASSERT(SUCCESS == db_read_first_row_id(rollback_db_filename,&row_id));
794 : :
795 : : unsigned char before_blob[512];
796 : 2 : int before_blob_size = 0;
797 : 2 : ASSERT(SUCCESS == db_read_stat_blob_by_row_id(rollback_db_filename,
798 : : row_id,
799 : : before_blob,
800 : : sizeof(before_blob),
801 : : &before_blob_size));
802 : :
803 : 2 : int v1_rows_before = 0;
804 : 2 : ASSERT(SUCCESS == db_read_files_count_with_blob_size(rollback_db_filename,
805 : : (int)sizeof(CmpctStat_v1),
806 : : &v1_rows_before));
807 : :
808 : 2 : ASSERT(SUCCESS == db_create_abort_on_second_stat_update_trigger(rollback_db_filename));
809 : :
810 : 2 : const char *arguments = "--check-level=QUICK --compare --update 0015_database_v4_reference.db 0015_database_v3_rollback.db";
811 : :
812 : 2 : m_create(char,result,MEMORY_STRING);
813 : 2 : m_create(char,pattern,MEMORY_STRING);
814 : :
815 : 2 : ASSERT(SUCCESS == runit(arguments,result,NULL,FAILURE,ALLOW_BOTH));
816 : 2 : const char *filename = "templates/0015_017.txt";
817 : 2 : ASSERT(SUCCESS == get_file_content(filename,pattern));
818 : 2 : ASSERT(SUCCESS == match_pattern(result,pattern,filename));
819 : :
820 : 2 : int db_version = 0;
821 : 2 : ASSERT(SUCCESS == read_db_version_from_metadata(rollback_db_filename,&db_version));
822 : 2 : ASSERT(db_version == 3);
823 : :
824 : : unsigned char after_blob[512];
825 : 2 : int after_blob_size = 0;
826 : 2 : ASSERT(SUCCESS == db_read_stat_blob_by_row_id(rollback_db_filename,
827 : : row_id,
828 : : after_blob,
829 : : sizeof(after_blob),
830 : : &after_blob_size));
831 : :
832 : 2 : ASSERT(before_blob_size == after_blob_size);
833 : :
834 [ + - ]: 2 : if(before_blob_size > 0)
835 : : {
836 : 2 : ASSERT(0 == memcmp(before_blob,after_blob,(size_t)before_blob_size));
837 : : }
838 : :
839 : 2 : int v1_rows_after = 0;
840 : 2 : ASSERT(SUCCESS == db_read_files_count_with_blob_size(rollback_db_filename,
841 : : (int)sizeof(CmpctStat_v1),
842 : : &v1_rows_after));
843 : 2 : ASSERT(v1_rows_before == v1_rows_after);
844 : :
845 : 2 : ASSERT(SUCCESS == delete_path(rollback_db_filename));
846 : 2 : ASSERT(SUCCESS == delete_path(reference_db_filename));
847 : :
848 : 2 : m_del(result);
849 : 2 : m_del(pattern);
850 : :
851 : 2 : RETURN_STATUS;
852 : : }
853 : :
854 : : /**
855 : : * @brief Warn when metadata declares a DB version newer than supported
856 : : *
857 : : * Verify both testing and non-testing modes keep the future DB unchanged
858 : : */
859 : 2 : Return test0015_19(void)
860 : : {
861 : 2 : INITTEST;
862 : :
863 : 2 : ASSERT(SUCCESS == set_environment_variable("TESTING","true"));
864 : :
865 : 2 : const char *db_filename = "0015_database_future_version.db";
866 : 2 : const char *arguments = "--database=0015_database_future_version.db "
867 : : "tests/fixtures/diffs/diff1";
868 : :
869 : 2 : m_create(char,result,MEMORY_STRING);
870 : 2 : m_create(char,pattern,MEMORY_STRING);
871 : :
872 : 2 : ASSERT(SUCCESS == runit(arguments,NULL,NULL,COMPLETED,ALLOW_BOTH));
873 : 2 : ASSERT(SUCCESS == set_db_version_in_metadata(db_filename,CURRENT_DB_VERSION + 1));
874 : :
875 : 2 : int db_version = 0;
876 : 2 : ASSERT(SUCCESS == read_db_version_from_metadata(db_filename,&db_version));
877 : 2 : ASSERT(db_version == CURRENT_DB_VERSION + 1);
878 : :
879 : 2 : arguments = "--check-level=QUICK --database=0015_database_future_version.db "
880 : : "tests/fixtures/diffs/diff1";
881 : :
882 : 2 : ASSERT(SUCCESS == runit(arguments,result,NULL,WARNING,ALLOW_BOTH));
883 : :
884 : 2 : const char *filename = "templates/0015_018_1.txt";
885 : :
886 : 2 : ASSERT(SUCCESS == get_file_content(filename,pattern));
887 : 2 : ASSERT(SUCCESS == match_pattern(result,pattern,filename));
888 : :
889 : 2 : ASSERT(SUCCESS == read_db_version_from_metadata(db_filename,&db_version));
890 : 2 : ASSERT(db_version == CURRENT_DB_VERSION + 1);
891 : :
892 : 2 : ASSERT(SUCCESS == set_environment_variable("TESTING","false"));
893 : 2 : ASSERT(SUCCESS == runit(arguments,result,NULL,WARNING,ALLOW_BOTH));
894 : :
895 : 2 : filename = "templates/0015_018_2.txt";
896 : :
897 : 2 : ASSERT(SUCCESS == get_file_content(filename,pattern));
898 : 2 : ASSERT(SUCCESS == match_pattern(result,pattern,filename));
899 : :
900 : 2 : ASSERT(SUCCESS == delete_path(db_filename));
901 : :
902 : 2 : m_del(result);
903 : 2 : m_del(pattern);
904 : :
905 : 2 : RETURN_STATUS;
906 : : }
907 : :
908 : : /**
909 : : * @brief Exercise DB upgrade and migration regressions across legacy formats
910 : : *
911 : : * This scenario covers primary DB upgrades from legacy versions 0 through 3,
912 : : * comparison-driven upgrades, default DB creation, UTF-8 filename handling,
913 : : * checksum compatibility against a legacy v4 reference DB, resilient handling
914 : : * of corrupted legacy stat blobs, rollback on forced SQLite migration failure,
915 : : * and warning behavior for a DB that reports a future metadata version
916 : : */
917 : 2 : Return test0015(void)
918 : : {
919 : 2 : INITTEST;
920 : :
921 : 2 : TEST(test0015_1,"Upgrade a DB from v0 to the current version. Error handling");
922 : 2 : TEST(test0015_2,"Upgrade a DB from v0 to the current version as the primary database");
923 : 2 : TEST(test0015_3,"Upgrade a DB from v0 to the current version with --watch-timestamps");
924 : 2 : TEST(test0015_4,"Verify that the upgraded v0 DB is actually at the current version");
925 : 2 : TEST(test0015_5,"Create default name database");
926 : 2 : TEST(test0015_6,"Attempting an upgrade with a single --compare parameter");
927 : 2 : TEST(test0015_7,"Upgrading from 0 to the last version using the --compare and --update");
928 : 2 : TEST(test0015_8,"Upgrade a DB from v1 to the current version as the primary database");
929 : 2 : TEST(test0015_9,"Verify that the upgraded v1 DB is actually at the current version");
930 : 2 : TEST(test0015_10,"Upgrading from 1 to the last version using the --compare and --update");
931 : 2 : TEST(test0015_11,"Upgrade a DB from v2 to the current version as the primary database");
932 : 2 : TEST(test0015_12,"Upgrading from 2 to the last version using the --compare and --update");
933 : 2 : TEST(test0015_13,"Upgrading from 3 with UTF-8 name to the last version using the --update");
934 : 2 : TEST(test0015_14,"Upgrading from 3 to the last version using the --compare and --update");
935 : 2 : TEST(test0015_15,"Create and compare DBs with UTF-8 names and checksums from legacy DB");
936 : 2 : TEST(test0015_16,"Corrupted v0 stat blob does not break the full upgrade");
937 : 2 : TEST(test0015_17,"Corrupted v3 stat blob does not break the full upgrade");
938 : 2 : TEST(test0015_18,"Forced SQLite failure triggers rollback during 3->4 migration");
939 : 2 : TEST(test0015_19,"Fresh DB with forced future version returns warning and stays unchanged");
940 : :
941 : 2 : RETURN_STATUS;
942 : : }
|