LCOV - code coverage report
Current view: top level - tests/src - test0016.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 100.0 % 231 231
Test Date: 2026-10-20 14:27:43 Functions: 100.0 % 5 5
Branches: - 0 0

             Branch data     Line data    Source code
       1                 :             : #include "sute.h"
       2                 :             : 
       3                 :             : /**
       4                 :             :  * @brief Verify updates and DB comparisons after content, deletion, and permission changes
       5                 :             :  *
       6                 :             :  * Content growth triggers rehashing with or without --watch-timestamps.
       7                 :             :  * A permission-only change updates metadata without this option and rehashes
       8                 :             :  * unchanged content when it is enabled.
       9                 :             :  * Both database comparisons report the deleted file and the content change.
      10                 :             :  * Full output is checked with TESTING enabled and disabled
      11                 :             :  *
      12                 :             :  * @return Return status code
      13                 :             :  */
      14                 :           2 : static Return test0016_1(void)
      15                 :             : {
      16                 :           2 :         INITTEST;
      17                 :             : 
      18                 :           2 :         m_create(char,pattern,MEMORY_STRING);
      19                 :             : 
      20                 :             :         // Create memory for the result
      21                 :           2 :         m_create(char,result,MEMORY_STRING);
      22                 :           2 :         m_create(char,chunk,MEMORY_STRING);
      23                 :             : 
      24                 :           2 :         m_create(char,target_path,MEMORY_STRING);
      25                 :           2 :         struct stat stat_before = {0};
      26                 :           2 :         struct stat stat_after = {0};
      27                 :             : 
      28                 :             :         // Preparation for the test
      29                 :           2 :         const char *diff1_fixture_path = "tests/fixtures/diffs/diff1";
      30                 :           2 :         const char *permission_file_path = "tests/fixtures/diffs/diff1/2/AAA/BBB/CZC/a.txt";
      31                 :           2 :         ASSERT(SUCCESS == prepare_mutable_fixture(diff1_fixture_path));
      32                 :             : 
      33                 :           2 :         ASSERT(SUCCESS == set_environment_variable("TESTING","true"));
      34                 :             : 
      35                 :           2 :         ASSERT(SUCCESS == runit("--database=database1.db tests/fixtures/diffs/diff1",chunk,NULL,COMPLETED,ALLOW_BOTH));
      36                 :           2 :         ASSERT(SUCCESS == m_copy(result,chunk));
      37                 :             : 
      38                 :           2 :         ASSERT(SUCCESS == copy_path("database1.db","database2.db"));
      39                 :             : 
      40                 :           2 :         ASSERT(SUCCESS == add_string_to("PWOEUNVSODNLKUHGE","tests/fixtures/diffs/diff1/1/AAA/BCB/CCC/a.txt"));
      41                 :             : 
      42                 :           2 :         ASSERT(SUCCESS == delete_path("tests/fixtures/diffs/diff1/path2/AAA/ZAW/D/e/f/b_file.txt"));
      43                 :             : 
      44                 :             :         // Toggle owner write permission to change ctime while preserving content, mtime, and read access
      45                 :           2 :         ASSERT(SUCCESS == construct_path(permission_file_path,target_path));
      46                 :           2 :         ASSERT(SUCCESS == get_file_stat(m_text(target_path),&stat_before));
      47                 :           2 :         unsigned int changed_mode = ((unsigned int)stat_before.st_mode & 07777U) ^ S_IWUSR;
      48                 :           2 :         ASSERT(SUCCESS == change_mode(permission_file_path,changed_mode));
      49                 :           2 :         ASSERT(SUCCESS == get_file_stat(m_text(target_path),&stat_after));
      50                 :           2 :         ASSERT(stat_after.st_mode == (stat_before.st_mode ^ S_IWUSR));
      51                 :           2 :         ASSERT(stat_before.st_ctim.tv_sec != stat_after.st_ctim.tv_sec
      52                 :             :                 || stat_before.st_ctim.tv_nsec != stat_after.st_ctim.tv_nsec);
      53                 :           2 :         ASSERT(stat_before.st_mtim.tv_sec == stat_after.st_mtim.tv_sec
      54                 :             :                 && stat_before.st_mtim.tv_nsec == stat_after.st_mtim.tv_nsec);
      55                 :           2 :         ASSERT(stat_before.st_size == stat_after.st_size);
      56                 :           2 :         ASSERT(stat_before.st_blocks == stat_after.st_blocks);
      57                 :             : 
      58                 :             :         // Update without --watch-timestamps: rehash changed content, update ctime-only
      59                 :             :         // metadata without rehashing, and remove the deleted file's DB record
      60                 :           2 :         ASSERT(SUCCESS == runit("--update --check-level=QUICK --database=database1.db tests/fixtures/diffs/diff1",chunk,NULL,COMPLETED,ALLOW_BOTH));
      61                 :           2 :         ASSERT(SUCCESS == m_concat_strings(result,chunk));
      62                 :             : 
      63                 :             :         // Compare against the original DB: report the deleted file and the changed checksum
      64                 :           2 :         const char *compare_arguments = "--compare --check-level=QUICK database1.db database2.db";
      65                 :           2 :         ASSERT(SUCCESS == runit(compare_arguments,chunk,NULL,COMPLETED,ALLOW_BOTH));
      66                 :           2 :         ASSERT(SUCCESS == m_concat_strings(result,chunk));
      67                 :             : 
      68                 :             :         // Restore the original DB so the next update processes the same changes
      69                 :           2 :         ASSERT(SUCCESS == copy_path("database2.db","database1.db"));
      70                 :             : 
      71                 :             :         // Update with --watch-timestamps: rehash the file whose ctime changed as well
      72                 :           2 :         const char *watch_update_arguments = "--watch-timestamps --update --check-level=QUICK --database=database1.db tests/fixtures/diffs/diff1";
      73                 :           2 :         ASSERT(SUCCESS == runit(watch_update_arguments,chunk,NULL,COMPLETED,ALLOW_BOTH));
      74                 :           2 :         ASSERT(SUCCESS == m_concat_strings(result,chunk));
      75                 :             : 
      76                 :             :         // Verify that rehashing the permission-only change adds no checksum difference
      77                 :           2 :         ASSERT(SUCCESS == runit(compare_arguments,chunk,NULL,COMPLETED,ALLOW_BOTH));
      78                 :           2 :         ASSERT(SUCCESS == m_concat_strings(result,chunk));
      79                 :             : 
      80                 :             :         // Check the complete combined output for creation, both updates, and both comparisons
      81                 :           2 :         const char *filename = "templates/0016_001_1.txt";
      82                 :           2 :         ASSERT(SUCCESS == get_file_content(filename,pattern));
      83                 :           2 :         ASSERT(SUCCESS == match_pattern(result,pattern,filename));
      84                 :             : 
      85                 :             :         // Clean to use it iteratively
      86                 :           2 :         m_del(pattern);
      87                 :           2 :         m_del(result);
      88                 :           2 :         m_del(chunk);
      89                 :             : 
      90                 :           2 :         ASSERT(SUCCESS == set_environment_variable("TESTING","false"));
      91                 :             : 
      92                 :           2 :         ASSERT(SUCCESS == copy_path("database2.db","database1.db"));
      93                 :             : 
      94                 :           2 :         ASSERT(SUCCESS == runit("--update --check-level=QUICK --database=database1.db tests/fixtures/diffs/diff1",chunk,NULL,COMPLETED,ALLOW_BOTH));
      95                 :           2 :         ASSERT(SUCCESS == m_copy(result,chunk));
      96                 :             : 
      97                 :           2 :         ASSERT(SUCCESS == runit(compare_arguments,chunk,NULL,COMPLETED,ALLOW_BOTH));
      98                 :           2 :         ASSERT(SUCCESS == m_concat_strings(result,chunk));
      99                 :             : 
     100                 :           2 :         ASSERT(SUCCESS == copy_path("database2.db","database1.db"));
     101                 :             : 
     102                 :           2 :         ASSERT(SUCCESS == runit(watch_update_arguments,chunk,NULL,COMPLETED,ALLOW_BOTH));
     103                 :           2 :         ASSERT(SUCCESS == m_concat_strings(result,chunk));
     104                 :             : 
     105                 :           2 :         ASSERT(SUCCESS == runit(compare_arguments,chunk,NULL,COMPLETED,ALLOW_BOTH));
     106                 :           2 :         ASSERT(SUCCESS == m_concat_strings(result,chunk));
     107                 :             : 
     108                 :           2 :         filename = "templates/0016_001_2.txt";
     109                 :           2 :         ASSERT(SUCCESS == get_file_content(filename,pattern));
     110                 :           2 :         ASSERT(SUCCESS == match_pattern(result,pattern,filename));
     111                 :             : 
     112                 :             :         // Clean to use it iteratively
     113                 :           2 :         m_del(pattern);
     114                 :           2 :         m_del(result);
     115                 :           2 :         m_del(chunk);
     116                 :             : 
     117                 :             :         // Clean up test results
     118                 :           2 :         ASSERT(SUCCESS == delete_path("database1.db"));
     119                 :           2 :         ASSERT(SUCCESS == delete_path("database2.db"));
     120                 :           2 :         ASSERT(SUCCESS == restore_mutable_fixture(diff1_fixture_path));
     121                 :           2 :         m_del(target_path);
     122                 :             : 
     123                 :           2 :         RETURN_STATUS;
     124                 :             : }
     125                 :             : 
     126                 :             : /**
     127                 :             :  * @brief Rehash same-size content changes when mtime changes without --watch-timestamps
     128                 :             :  *
     129                 :             :  * CLI and configuration runs start from the same database and changed file.
     130                 :             :  * Both runs must produce the same full output, checksum, and stored timestamps.
     131                 :             :  * Only the database is restored before the configuration run
     132                 :             :  *
     133                 :             :  * @return Return status code
     134                 :             :  */
     135                 :           2 : static Return test0016_2(void)
     136                 :             : {
     137                 :           2 :         INITTEST;
     138                 :             : 
     139                 :           2 :         const char *fixture_path = "0016_mtime";
     140                 :           2 :         const char *file_path = "0016_mtime/file.txt";
     141                 :           2 :         const char *db_filename = "0016_mtime.db";
     142                 :           2 :         const char *db_backup_filename = "0016_mtime_before_update.db";
     143                 :           2 :         m_create(char,target_path,MEMORY_STRING);
     144                 :           2 :         struct stat stat_before = {0};
     145                 :           2 :         struct stat stat_after = {0};
     146                 :           2 :         CmpctStat saved_stat = {0};
     147                 :           2 :         sqlite3_int64 offset = -1;
     148                 :           2 :         unsigned char checksum_before[SHA512_DIGEST_LENGTH] = {0};
     149                 :           2 :         unsigned char checksum_after[SHA512_DIGEST_LENGTH] = {0};
     150                 :             : 
     151                 :             :         // Create a three-byte file containing "aaa" and store its checksum in the DB
     152                 :           2 :         ASSERT(SUCCESS == set_environment_variable("TESTING","false"));
     153                 :           2 :         ASSERT(SUCCESS == create_directory(fixture_path));
     154                 :           2 :         ASSERT(SUCCESS == replase_to_string("aaa",file_path));
     155                 :           2 :         ASSERT(SUCCESS == construct_path(file_path,target_path));
     156                 :           2 :         ASSERT(SUCCESS == runit("--database=0016_mtime.db 0016_mtime",NULL,NULL,COMPLETED,ALLOW_BOTH));
     157                 :             : 
     158                 :             :         // Save the completed checksum and verify it independently before changing the file
     159                 :           2 :         ASSERT(SUCCESS == read_final_sha512_from_db(db_filename,"file.txt",&offset,checksum_before));
     160                 :           2 :         ASSERT(offset == 0);
     161                 :           2 :         ASSERT(SUCCESS == db_final_sha512_matches_file(db_filename,"file.txt",m_text(target_path)));
     162                 :           2 :         ASSERT(SUCCESS == get_file_stat(m_text(target_path),&stat_before));
     163                 :             : 
     164                 :             :         // Use the saved mtime as a deterministic reference and preserve atime
     165                 :           2 :         struct timespec file_times[2] = {{0}};
     166                 :           2 :         file_times[0].tv_nsec = UTIME_OMIT;
     167                 :           2 :         file_times[1] = stat_before.st_mtim;
     168                 :           2 :         file_times[1].tv_sec++;
     169                 :             : 
     170                 :             :         // Replace "aaa" with "bbb" and force mtime one second past its original value
     171                 :           2 :         ASSERT(SUCCESS == replase_to_string("bbb",file_path));
     172                 :           2 :         ASSERT(utimensat(0,m_text(target_path),file_times,0) == 0);
     173                 :             : 
     174                 :             :         // Confirm that the file is still three bytes long and its mtime has changed
     175                 :           2 :         ASSERT(SUCCESS == get_file_stat(m_text(target_path),&stat_after));
     176                 :           2 :         ASSERT(stat_before.st_size == 3 && stat_after.st_size == 3);
     177                 :           2 :         ASSERT(stat_before.st_mtim.tv_sec != stat_after.st_mtim.tv_sec
     178                 :             :                 || stat_before.st_mtim.tv_nsec != stat_after.st_mtim.tv_nsec);
     179                 :             : 
     180                 :             :         // Preserve the database before either target update
     181                 :           2 :         ASSERT(SUCCESS == copy_path(db_filename,db_backup_filename));
     182                 :             : 
     183                 :             :         // Update without --watch-timestamps to exercise default mtime detection
     184                 :           2 :         const char *filename = "templates/0016_002_1.txt";
     185                 :           2 :         const struct argument_test_case cases[] = {
     186                 :             :                 {"--update --check-level=QUICK --database=0016_mtime.db 0016_mtime",
     187                 :             :                  COMPLETED,filename,STDERR_MUST_BE_EMPTY},
     188                 :             :                 {0}
     189                 :             :         };
     190                 :             : 
     191                 :             :         // Check the full output for a rehash of file.txt and exactly three hashed bytes
     192                 :           2 :         ASSERT(SUCCESS == run_and_match_templates(cases,NULL,NULL));
     193                 :             : 
     194                 :             :         // Verify that the new checksum is complete and differs from the checksum of "aaa"
     195                 :           2 :         ASSERT(SUCCESS == read_final_sha512_from_db(db_filename,"file.txt",&offset,checksum_after));
     196                 :           2 :         ASSERT(offset == 0);
     197                 :           2 :         ASSERT(memcmp(checksum_before,checksum_after,sizeof(checksum_before)) != 0);
     198                 :             : 
     199                 :             :         // Independently hash "bbb" with Monocypher and compare it with the stored checksum
     200                 :           2 :         ASSERT(SUCCESS == db_final_sha512_matches_file(db_filename,"file.txt",m_text(target_path)));
     201                 :             : 
     202                 :             :         // Verify that the DB contains the file's updated mtime and ctime
     203                 :           2 :         ASSERT(SUCCESS == db_read_cmpctstat_by_relative_path(db_filename,"file.txt",&saved_stat));
     204                 :           2 :         ASSERT(cmpctstat_matches_stat_timestamps(&saved_stat,&stat_after));
     205                 :             : 
     206                 :             :         /* Restore only the database so the configuration run processes the same
     207                 :             :            changed content and timestamps, then repeat all result checks */
     208                 :           2 :         ASSERT(SUCCESS == copy_path(db_backup_filename,db_filename));
     209                 :           2 :         ASSERT(SUCCESS == copy_path("tests/templates/0016_002_1.conf","0016_002_1.conf"));
     210                 :           2 :         const struct argument_test_case file_cases[] = {
     211                 :             :                 {"--config=0016_002_1.conf",COMPLETED,filename,STDERR_MUST_BE_EMPTY},
     212                 :             :                 {0}
     213                 :             :         };
     214                 :           2 :         ASSERT(SUCCESS == run_and_match_templates(file_cases,NULL,NULL));
     215                 :           2 :         ASSERT(SUCCESS == read_final_sha512_from_db(db_filename,"file.txt",&offset,checksum_after));
     216                 :           2 :         ASSERT(offset == 0);
     217                 :           2 :         ASSERT(memcmp(checksum_before,checksum_after,sizeof(checksum_before)) != 0);
     218                 :           2 :         ASSERT(SUCCESS == db_final_sha512_matches_file(db_filename,"file.txt",m_text(target_path)));
     219                 :           2 :         ASSERT(SUCCESS == db_read_cmpctstat_by_relative_path(db_filename,"file.txt",&saved_stat));
     220                 :           2 :         ASSERT(cmpctstat_matches_stat_timestamps(&saved_stat,&stat_after));
     221                 :             : 
     222                 :             :         // Remove the temporary DB and files, then release the test buffers
     223                 :           2 :         call(delete_path_if_present("0016_002_1.conf"));
     224                 :           2 :         call(delete_path_if_present(db_backup_filename));
     225                 :           2 :         call(delete_path_if_present(db_filename));
     226                 :           2 :         call(delete_path_if_present(fixture_path));
     227                 :           2 :         m_del(target_path);
     228                 :             : 
     229                 :           2 :         RETURN_STATUS;
     230                 :             : }
     231                 :             : 
     232                 :             : /**
     233                 :             :  * @brief Update metadata without reading file content after a permission change without --watch-timestamps
     234                 :             :  *
     235                 :             :  * @return Return status code
     236                 :             :  */
     237                 :           2 : static Return test0016_3(void)
     238                 :             : {
     239                 :           2 :         INITTEST;
     240                 :             : 
     241                 :           2 :         const char *fixture_path = "0016_chmod";
     242                 :           2 :         const char *file_path = "0016_chmod/file.txt";
     243                 :           2 :         const char *db_filename = "0016_chmod.db";
     244                 :           2 :         m_create(char,target_path,MEMORY_STRING);
     245                 :           2 :         m_create(char,result,MEMORY_STRING);
     246                 :           2 :         m_create(char,pattern,MEMORY_STRING);
     247                 :           2 :         struct stat stat_before = {0};
     248                 :           2 :         struct stat stat_after = {0};
     249                 :           2 :         CmpctStat saved_stat = {0};
     250                 :           2 :         sqlite3_int64 offset = -1;
     251                 :           2 :         unsigned char checksum_before[SHA512_DIGEST_LENGTH] = {0};
     252                 :           2 :         unsigned char checksum_after[SHA512_DIGEST_LENGTH] = {0};
     253                 :             : 
     254                 :           2 :         ASSERT(SUCCESS == set_environment_variable("TESTING","false"));
     255                 :           2 :         ASSERT(SUCCESS == create_directory(fixture_path));
     256                 :           2 :         ASSERT(SUCCESS == replase_to_string("aaa",file_path));
     257                 :           2 :         ASSERT(SUCCESS == change_mode(file_path,0600));
     258                 :           2 :         ASSERT(SUCCESS == construct_path(file_path,target_path));
     259                 :           2 :         ASSERT(SUCCESS == runit("--database=0016_chmod.db 0016_chmod",NULL,NULL,COMPLETED,ALLOW_BOTH));
     260                 :           2 :         ASSERT(SUCCESS == read_final_sha512_from_db(db_filename,"file.txt",&offset,checksum_before));
     261                 :           2 :         ASSERT(offset == 0);
     262                 :           2 :         ASSERT(SUCCESS == get_file_stat(m_text(target_path),&stat_before));
     263                 :             : 
     264                 :             :         // Keep the file readable while changing ctime without modifying content or mtime
     265                 :           2 :         ASSERT(SUCCESS == change_mode(file_path,0644));
     266                 :           2 :         ASSERT(SUCCESS == get_file_stat(m_text(target_path),&stat_after));
     267                 :           2 :         ASSERT(stat_before.st_mode != stat_after.st_mode);
     268                 :           2 :         ASSERT(stat_before.st_ctim.tv_sec != stat_after.st_ctim.tv_sec
     269                 :             :                 || stat_before.st_ctim.tv_nsec != stat_after.st_ctim.tv_nsec);
     270                 :           2 :         ASSERT(stat_before.st_mtim.tv_sec == stat_after.st_mtim.tv_sec
     271                 :             :                 && stat_before.st_mtim.tv_nsec == stat_after.st_mtim.tv_nsec);
     272                 :           2 :         ASSERT(stat_before.st_size == stat_after.st_size);
     273                 :           2 :         ASSERT(stat_before.st_blocks == stat_after.st_blocks);
     274                 :             : 
     275                 :             :         // Update without --watch-timestamps and check the full output for
     276                 :             :         // a metadata-only update with zero bytes hashed by the application
     277                 :           2 :         ASSERT(SUCCESS == runit("--update --check-level=QUICK --database=0016_chmod.db 0016_chmod",result,NULL,COMPLETED,ALLOW_BOTH));
     278                 :           2 :         const char *filename = "templates/0016_002_2.txt";
     279                 :           2 :         ASSERT(SUCCESS == get_file_content(filename,pattern));
     280                 :           2 :         ASSERT(SUCCESS == match_pattern(result,pattern,filename));
     281                 :             : 
     282                 :             :         // A zero offset denotes a completed checksum, which must retain its original value
     283                 :           2 :         ASSERT(SUCCESS == read_final_sha512_from_db(db_filename,"file.txt",&offset,checksum_after));
     284                 :           2 :         ASSERT(offset == 0);
     285                 :           2 :         ASSERT(memcmp(checksum_before,checksum_after,sizeof(checksum_before)) == 0);
     286                 :             : 
     287                 :             :         // Independently hash the file in the test to verify the checksum stored in the DB
     288                 :           2 :         ASSERT(SUCCESS == db_final_sha512_matches_file(db_filename,"file.txt",m_text(target_path)));
     289                 :             : 
     290                 :             :         // Confirm that the DB saved the changed ctime and preserved the file's mtime
     291                 :           2 :         ASSERT(SUCCESS == db_read_cmpctstat_by_relative_path(db_filename,"file.txt",&saved_stat));
     292                 :           2 :         ASSERT(cmpctstat_matches_stat_timestamps(&saved_stat,&stat_after));
     293                 :             : 
     294                 :           2 :         call(delete_path_if_present(db_filename));
     295                 :           2 :         call(delete_path_if_present(fixture_path));
     296                 :           2 :         m_del(pattern);
     297                 :           2 :         m_del(result);
     298                 :           2 :         m_del(target_path);
     299                 :             : 
     300                 :           2 :         RETURN_STATUS;
     301                 :             : }
     302                 :             : 
     303                 :             : /**
     304                 :             :  * @brief Rehash unchanged content after a permission change when --watch-timestamps watches ctime
     305                 :             :  *
     306                 :             :  * CLI and configuration updates start from the same saved database after a
     307                 :             :  * single permission change. Both check the full output, checksum, and stored
     308                 :             :  * timestamps against the same prepared file
     309                 :             :  *
     310                 :             :  * @return Return status code
     311                 :             :  */
     312                 :           2 : static Return test0016_4(void)
     313                 :             : {
     314                 :           2 :         INITTEST;
     315                 :             : 
     316                 :           2 :         const char *fixture_path = "0016_watch_chmod";
     317                 :           2 :         const char *file_path = "0016_watch_chmod/file.txt";
     318                 :           2 :         const char *db_filename = "0016_watch_chmod.db";
     319                 :           2 :         const char *db_backup_filename = "0016_watch_chmod_before_update.db";
     320                 :           2 :         m_create(char,target_path,MEMORY_STRING);
     321                 :           2 :         struct stat stat_before = {0};
     322                 :           2 :         struct stat stat_after = {0};
     323                 :           2 :         CmpctStat saved_stat = {0};
     324                 :           2 :         sqlite3_int64 offset = -1;
     325                 :           2 :         unsigned char checksum_before[SHA512_DIGEST_LENGTH] = {0};
     326                 :           2 :         unsigned char checksum_after[SHA512_DIGEST_LENGTH] = {0};
     327                 :             : 
     328                 :           2 :         ASSERT(SUCCESS == set_environment_variable("TESTING","false"));
     329                 :           2 :         ASSERT(SUCCESS == create_directory(fixture_path));
     330                 :           2 :         ASSERT(SUCCESS == replase_to_string("aaa",file_path));
     331                 :           2 :         ASSERT(SUCCESS == change_mode(file_path,0644));
     332                 :           2 :         ASSERT(SUCCESS == construct_path(file_path,target_path));
     333                 :           2 :         ASSERT(SUCCESS == runit("--database=0016_watch_chmod.db 0016_watch_chmod",NULL,NULL,COMPLETED,ALLOW_BOTH));
     334                 :           2 :         ASSERT(SUCCESS == read_final_sha512_from_db(db_filename,"file.txt",&offset,checksum_before));
     335                 :           2 :         ASSERT(offset == 0);
     336                 :           2 :         ASSERT(SUCCESS == get_file_stat(m_text(target_path),&stat_before));
     337                 :             : 
     338                 :             :         // A fresh permission change supplies ctime drift for the watched update
     339                 :           2 :         ASSERT(SUCCESS == change_mode(file_path,0600));
     340                 :           2 :         ASSERT(SUCCESS == get_file_stat(m_text(target_path),&stat_after));
     341                 :           2 :         ASSERT(stat_before.st_mode != stat_after.st_mode);
     342                 :           2 :         ASSERT(stat_before.st_ctim.tv_sec != stat_after.st_ctim.tv_sec
     343                 :             :                 || stat_before.st_ctim.tv_nsec != stat_after.st_ctim.tv_nsec);
     344                 :           2 :         ASSERT(stat_before.st_mtim.tv_sec == stat_after.st_mtim.tv_sec
     345                 :             :                 && stat_before.st_mtim.tv_nsec == stat_after.st_mtim.tv_nsec);
     346                 :           2 :         ASSERT(stat_before.st_size == stat_after.st_size);
     347                 :           2 :         ASSERT(stat_before.st_blocks == stat_after.st_blocks);
     348                 :             : 
     349                 :             :         /* Preserve the database before either update records the changed ctime */
     350                 :           2 :         ASSERT(SUCCESS == copy_path(db_filename,db_backup_filename));
     351                 :             : 
     352                 :           2 :         const char *filename = "templates/0016_002_3.txt";
     353                 :           2 :         const struct argument_test_case cases[] = {
     354                 :             :                 {"--update --check-level=QUICK --watch-timestamps --database=0016_watch_chmod.db 0016_watch_chmod",
     355                 :             :                  COMPLETED,filename,STDERR_MUST_BE_EMPTY},
     356                 :             :                 {0}
     357                 :             :         };
     358                 :           2 :         ASSERT(SUCCESS == run_and_match_templates(cases,NULL,NULL));
     359                 :           2 :         ASSERT(SUCCESS == read_final_sha512_from_db(db_filename,"file.txt",&offset,checksum_after));
     360                 :           2 :         ASSERT(offset == 0);
     361                 :           2 :         ASSERT(memcmp(checksum_before,checksum_after,sizeof(checksum_before)) == 0);
     362                 :           2 :         ASSERT(SUCCESS == db_final_sha512_matches_file(db_filename,"file.txt",m_text(target_path)));
     363                 :           2 :         ASSERT(SUCCESS == db_read_cmpctstat_by_relative_path(db_filename,"file.txt",&saved_stat));
     364                 :           2 :         ASSERT(cmpctstat_matches_stat_timestamps(&saved_stat,&stat_after));
     365                 :             : 
     366                 :             :         /* Restore only the database so the configuration run sees the same permission
     367                 :             :            change, then repeat the checksum and timestamp checks */
     368                 :           2 :         ASSERT(SUCCESS == copy_path(db_backup_filename,db_filename));
     369                 :           2 :         ASSERT(SUCCESS == copy_path("tests/templates/0016_002_3.conf","0016_002_3.conf"));
     370                 :           2 :         const struct argument_test_case file_cases[] = {
     371                 :             :                 {"--config=0016_002_3.conf",COMPLETED,filename,STDERR_MUST_BE_EMPTY},
     372                 :             :                 {0}
     373                 :             :         };
     374                 :           2 :         ASSERT(SUCCESS == run_and_match_templates(file_cases,NULL,NULL));
     375                 :           2 :         ASSERT(SUCCESS == read_final_sha512_from_db(db_filename,"file.txt",&offset,checksum_after));
     376                 :           2 :         ASSERT(offset == 0);
     377                 :           2 :         ASSERT(memcmp(checksum_before,checksum_after,sizeof(checksum_before)) == 0);
     378                 :           2 :         ASSERT(SUCCESS == db_final_sha512_matches_file(db_filename,"file.txt",m_text(target_path)));
     379                 :           2 :         ASSERT(SUCCESS == db_read_cmpctstat_by_relative_path(db_filename,"file.txt",&saved_stat));
     380                 :           2 :         ASSERT(cmpctstat_matches_stat_timestamps(&saved_stat,&stat_after));
     381                 :             : 
     382                 :           2 :         call(delete_path_if_present("0016_002_3.conf"));
     383                 :           2 :         call(delete_path_if_present(db_backup_filename));
     384                 :           2 :         call(delete_path_if_present(db_filename));
     385                 :           2 :         call(delete_path_if_present(fixture_path));
     386                 :           2 :         m_del(target_path);
     387                 :             : 
     388                 :           2 :         RETURN_STATUS;
     389                 :             : }
     390                 :             : 
     391                 :             : /**
     392                 :             :  * @brief Verify default and optional timestamp-driven checksum updates
     393                 :             :  *
     394                 :             :  * @return Return status code
     395                 :             :  */
     396                 :           2 : Return test0016(void)
     397                 :             : {
     398                 :           2 :         INITTEST;
     399                 :             : 
     400                 :           2 :         TEST(test0016_1,"Content, deletion, and permission updates with and without --watch-timestamps");
     401                 :           2 :         TEST(test0016_2,"A changed mtime rehashes same-size content without --watch-timestamps");
     402                 :           2 :         TEST(test0016_3,"Permission changes update metadata without hashing by default");
     403                 :           2 :         TEST(test0016_4,"Permission changes rehash content when --watch-timestamps watches ctime");
     404                 :             : 
     405                 :           2 :         RETURN_STATUS;
     406                 :             : }
        

Generated by: LCOV version 2.0-1