Branch data Line data Source code
1 : : #include "precizer.h"
2 : : #include <utime.h>
3 : :
4 : : #ifdef TESTITALL_TEST_HOOKS
5 : : /**
6 : : * @brief Test-only hook to simulate unexpected DB metadata change in dry-run mode.
7 : : *
8 : : * When enabled by environment variable
9 : : * `PRECIZER_TEST_DB_FILE_TIMESTAMPS_WILL_BUMPED=true`, this helper updates
10 : : * database file timestamps via utime(), forcing metadata drift before the final
11 : : * `stat()` comparison in db_check_changes().
12 : : *
13 : : * The hook is intentionally limited to dry-run on an existing primary DB file.
14 : : */
15 : 92 : static Return run_test_hook_bump_db_timestamps(void)
16 : : {
17 : : /* Status returned by this function through provide()
18 : : Default value assumes successful completion */
19 : 92 : Return status = SUCCESS;
20 : :
21 : 92 : const char *flag_value = getenv("PRECIZER_TEST_DB_FILE_TIMESTAMPS_WILL_BUMPED");
22 : :
23 [ + + + + ]: 92 : if(flag_value == NULL || strcmp(flag_value,"true") != 0)
24 : : {
25 : 90 : return(status);
26 : : }
27 : :
28 [ - + ]: 2 : if(config->dry_run != true)
29 : : {
30 : 0 : slog(ERROR,"Test hook failed: PRECIZER_TEST_DB_FILE_TIMESTAMPS_WILL_BUMPED requires --dry-run\n");
31 : 0 : return(FAILURE);
32 : : }
33 : :
34 [ - + ]: 2 : if(config->db_primary_file_exists != true)
35 : : {
36 : 0 : return(status);
37 : : }
38 : :
39 : 2 : const char *db_path = confstr(db_primary_file_path);
40 : :
41 [ + - - + ]: 2 : if(db_path == NULL || db_path[0] == '\0')
42 : : {
43 : 0 : slog(ERROR,"Test hook failed: database path is empty\n");
44 : 0 : return(FAILURE);
45 : : }
46 : :
47 [ - + ]: 2 : if(utime(db_path,NULL) != 0)
48 : : {
49 : 0 : slog(ERROR,"Test hook failed: unable to bump timestamps for %s\n",db_path);
50 : 0 : return(FAILURE);
51 : : }
52 : :
53 : 2 : slog(TESTING,"Test hook: database file timestamps were bumped for %s\n",confstr(db_file_name));
54 : :
55 : 2 : return(status);
56 : : }
57 : :
58 : : /**
59 : : * @brief Test-only hook to simulate missing DB metadata drift during update mode.
60 : : *
61 : : * When enabled by environment variable
62 : : * `PRECIZER_TEST_DB_FILE_STAT_WILL_BE_RESYNCED=true`, this helper overwrites
63 : : * the saved baseline stat (`config->db_file_stat`) with the current DB file
64 : : * stat. This forces metadata comparison in db_check_changes() to report
65 : : * IDENTICAL, even if the database was modified earlier in the same run.
66 : : *
67 : : * The hook is intentionally limited to non-dry-run mode and to cases where
68 : : * `config->db_primary_file_modified` is already true.
69 : : */
70 : 92 : static Return run_test_hook_resync_db_stat_baseline(const struct stat *db_current_stat)
71 : : {
72 : : /* Status returned by this function through provide()
73 : : Default value assumes successful completion */
74 : 92 : Return status = SUCCESS;
75 : :
76 : 92 : const char *flag_value = getenv("PRECIZER_TEST_DB_FILE_STAT_WILL_BE_RESYNCED");
77 : :
78 [ + + + + ]: 92 : if(flag_value == NULL || strcmp(flag_value,"true") != 0)
79 : : {
80 : 90 : return(status);
81 : : }
82 : :
83 [ - + ]: 2 : if(config->dry_run == true)
84 : : {
85 : 0 : slog(ERROR,"Test hook failed: PRECIZER_TEST_DB_FILE_STAT_WILL_BE_RESYNCED requires non-dry-run mode\n");
86 : 0 : return(FAILURE);
87 : : }
88 : :
89 [ - + ]: 2 : if(config->db_primary_file_exists != true)
90 : : {
91 : 0 : return(status);
92 : : }
93 : :
94 [ - + ]: 2 : if(config->db_primary_file_modified != true)
95 : : {
96 : 0 : slog(ERROR,"Test hook failed: baseline resync requested, but database modification flag is false\n");
97 : 0 : return(FAILURE);
98 : : }
99 : :
100 [ - + ]: 2 : if(db_current_stat == NULL)
101 : : {
102 : 0 : slog(ERROR,"Test hook failed: current database stat is unavailable\n");
103 : 0 : return(FAILURE);
104 : : }
105 : :
106 : : /*
107 : : * Intentionally corrupt the comparison baseline for test coverage:
108 : : * make "before" equal to "after" even after a real DB update.
109 : : */
110 : 2 : config->db_file_stat = *db_current_stat;
111 : :
112 : 2 : slog(TESTING,"Test hook: baseline database stat was resynced to current metadata for %s\n",confstr(db_file_name));
113 : :
114 : 2 : return(status);
115 : : }
116 : : #endif
117 : :
118 : 92 : Return db_check_changes(void)
119 : : {
120 : : /* Status returned by this function through provide()
121 : : Default value assumes successful completion */
122 : 92 : Return status = SUCCESS;
123 : :
124 : 92 : struct stat db_current_stat = {0};
125 : :
126 : : #ifdef TESTITALL_TEST_HOOKS
127 [ + - ]: 92 : if(SUCCESS == status)
128 : : {
129 : 92 : status = run_test_hook_bump_db_timestamps();
130 : : }
131 : : #endif
132 : :
133 : 92 : int rc = stat(confstr(db_primary_file_path),&db_current_stat);
134 : :
135 [ - + ]: 92 : if(rc < 0)
136 : : {
137 : 0 : report("Stat of %s failed with error code: %d",confstr(db_primary_file_path),rc);
138 : 0 : status = FAILURE;
139 : : }
140 : :
141 : : #ifdef TESTITALL_TEST_HOOKS
142 [ + - ]: 92 : if(SUCCESS == status)
143 : : {
144 : 92 : status = run_test_hook_resync_db_stat_baseline(&db_current_stat);
145 : : }
146 : : #endif
147 : :
148 : 92 : CmpctStat before = {0};
149 : 92 : CmpctStat after = {0};
150 : :
151 [ + - - + ]: 92 : run(stat_copy(&config->db_file_stat,&before));
152 [ + - - + ]: 92 : run(stat_copy(&db_current_stat,&after));
153 : :
154 : 92 : Changed changes = file_compare_metadata_equivalence(&before,&after);
155 : :
156 [ + + ]: 92 : if(IDENTICAL != changes)
157 : : {
158 [ + + ]: 58 : if(config->db_primary_file_modified == true)
159 : : {
160 : 56 : slog(EVERY,BOLD "The database file %s has been modified since the program was launched" RESET "\n",confstr(db_file_name));
161 : : } else {
162 : 2 : slog(ERROR,"Internal error: The database file %s has changed, but according to the global variable tracking modification status, this should not have happened!\n",confstr(db_file_name));
163 : :
164 [ + - ]: 2 : if(!(rational_logger_mode & SILENT))
165 : : {
166 : 2 : show_difference(changes,&before,&after);
167 : : }
168 : 2 : status = WARNING;
169 : : }
170 : : } else {
171 [ + + ]: 34 : if(config->db_primary_file_modified == true)
172 : : {
173 : 2 : slog(ERROR,"Internal error. The database file %s has NOT changed, but according to the state of the global variable tracking modifications, it should have!\n",confstr(db_file_name));
174 : 2 : status = WARNING;
175 : : } else {
176 : 32 : slog(EVERY,BOLD "The database file %s has NOT been modified since the program was launched" RESET "\n",confstr(db_file_name));
177 : : }
178 : : }
179 : :
180 : 92 : provide(status);
181 : : }
|