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

             Branch data     Line data    Source code
       1                 :             : #include "sute.h"
       2                 :             : 
       3                 :             : /**
       4                 :             :  * @brief Check the default hostname.db name and ignore scan roots during comparison
       5                 :             :  *
       6                 :             :  * @return Return status for database creation and both comparison scenarios
       7                 :             :  */
       8                 :           2 : Return test0003_1(void)
       9                 :             : {
      10                 :           2 :         INITTEST;
      11                 :             : 
      12                 :           2 :         ASSERT(SUCCESS == set_environment_variable("TESTING","true"));
      13                 :             : 
      14                 :           2 :         const char *arguments = "--progress tests/fixtures/diffs/diff1";
      15                 :             : 
      16                 :           2 :         ASSERT(SUCCESS == runit(arguments,NULL,NULL,COMPLETED,ALLOW_BOTH));
      17                 :             : 
      18                 :           2 :         arguments = "--progress --database=database2.db "
      19                 :             :                 "tests/fixtures/diffs/diff2";
      20                 :             : 
      21                 :           2 :         ASSERT(SUCCESS == runit(arguments,NULL,NULL,COMPLETED,ALLOW_BOTH));
      22                 :             : 
      23                 :             :         // Get the output of the application
      24                 :           2 :         const struct argument_test_case cases[] = {
      25                 :             :                 {"--compare $DBNAME database2.db",COMPLETED,"templates/0003_001.txt",STDERR_MUST_BE_EMPTY},
      26                 :             :                 /* Named roots must be discarded before compare validation and diagnostics */
      27                 :             :                 {"--path=/definitely/nonexistent/first --compare $DBNAME "
      28                 :             :                  "--path=/definitely/nonexistent/second database2.db",COMPLETED,"templates/0003_001.txt",STDERR_MUST_BE_EMPTY},
      29                 :             :                 {0}
      30                 :             :         };
      31                 :           2 :         const char *template = "%DB_NAME%";
      32                 :             : 
      33                 :           2 :         const char *replacement = getenv("DBNAME");  // Database name
      34                 :             : 
      35                 :           2 :         ASSERT(replacement != NULL);
      36                 :             : 
      37                 :           2 :         ASSERT(SUCCESS == run_and_match_templates(cases,template,replacement));
      38                 :             : 
      39                 :             :         // Clean up test results
      40                 :           2 :         ASSERT(SUCCESS == delete_path(replacement));
      41                 :           2 :         ASSERT(SUCCESS == delete_path("database2.db"));
      42                 :             : 
      43                 :           2 :         RETURN_STATUS;
      44                 :             : }
      45                 :             : 
      46                 :             : /**
      47                 :             :  * @brief Check usage output and successful completion without arguments
      48                 :             :  *
      49                 :             :  * @return Return status for the no-argument invocation
      50                 :             :  */
      51                 :           2 : Return test0003_2(void)
      52                 :             : {
      53                 :           2 :         INITTEST;
      54                 :             : 
      55                 :           2 :         const struct argument_test_case cases[] = {
      56                 :             :                 {"",COMPLETED,"templates/0003_003.txt","templates/0003_002.txt"},
      57                 :             :                 {0}
      58                 :             :         };
      59                 :             : 
      60                 :           2 :         ASSERT(SUCCESS == set_environment_variable("TESTING","true"));
      61                 :             : 
      62                 :           2 :         ASSERT(SUCCESS == run_and_match_templates(cases,NULL,NULL));
      63                 :             : 
      64                 :           2 :         RETURN_STATUS;
      65                 :             : 
      66                 :             : }
      67                 :             : 
      68                 :             : /**
      69                 :             :  * @brief Check that information modes complete successfully without PATH
      70                 :             :  *
      71                 :             :  * Run each command first without a default configuration file, then with an
      72                 :             :  * invalid default file. Both runs must produce the same output and status.
      73                 :             :  * Explicit file selections are also ignored for missing and malformed files
      74                 :             :  *
      75                 :             :  * @return Return status for help, usage, and version output
      76                 :             :  */
      77                 :           2 : Return test0003_3(void)
      78                 :             : {
      79                 :           2 :         INITTEST;
      80                 :           2 :         const struct argument_test_case cases[] = {
      81                 :             :                 {"--help",COMPLETED,"templates/0003_004.txt",STDERR_MUST_BE_EMPTY},
      82                 :             :                 {"-h",COMPLETED,"templates/0003_004.txt",STDERR_MUST_BE_EMPTY},
      83                 :             :                 {"'-?'",COMPLETED,"templates/0003_004.txt",STDERR_MUST_BE_EMPTY},
      84                 :             :                 {"--usage",COMPLETED,"templates/0003_005.txt",STDERR_MUST_BE_EMPTY},
      85                 :             :                 {"-z",COMPLETED,"templates/0003_005.txt",STDERR_MUST_BE_EMPTY},
      86                 :             :                 {"--version",COMPLETED,"templates/0003_006.txt",STDERR_MUST_BE_EMPTY},
      87                 :             :                 {"-V",COMPLETED,"templates/0003_006.txt",STDERR_MUST_BE_EMPTY},
      88                 :             :                 {"--version --version",COMPLETED,"templates/0003_006.txt",STDERR_MUST_BE_EMPTY},
      89                 :             :                 {0}
      90                 :             :         };
      91                 :           2 :         const struct argument_test_case config_cases[] = {
      92                 :             :                 {"--config=0003_003.conf --help",COMPLETED,"templates/0003_004.txt",STDERR_MUST_BE_EMPTY},
      93                 :             :                 {"--help --config=0003_003.conf",COMPLETED,"templates/0003_004.txt",STDERR_MUST_BE_EMPTY},
      94                 :             :                 {"-g0003_003.conf -h",COMPLETED,"templates/0003_004.txt",STDERR_MUST_BE_EMPTY},
      95                 :             :                 {"'-?g0003_003.conf'",COMPLETED,"templates/0003_004.txt",STDERR_MUST_BE_EMPTY},
      96                 :             :                 {"--config=0003_003.conf --usage",COMPLETED,"templates/0003_005.txt",STDERR_MUST_BE_EMPTY},
      97                 :             :                 {"-z -g 0003_003.conf",COMPLETED,"templates/0003_005.txt",STDERR_MUST_BE_EMPTY},
      98                 :             :                 {"--config=0003_003.conf --version",COMPLETED,"templates/0003_006.txt",STDERR_MUST_BE_EMPTY},
      99                 :             :                 {"-Vg0003_003.conf",COMPLETED,"templates/0003_006.txt",STDERR_MUST_BE_EMPTY},
     100                 :             :                 {"--config=0003_003.conf --help --version",COMPLETED,"templates/0003_004.txt",STDERR_MUST_BE_EMPTY},
     101                 :             :                 {"--config=0003_003.conf --compare --help",COMPLETED,"templates/0003_004.txt",STDERR_MUST_BE_EMPTY},
     102                 :             :                 {"--path=/definitely/nonexistent/path --config=0003_003.conf --version",COMPLETED,"templates/0003_006.txt",STDERR_MUST_BE_EMPTY},
     103                 :             :                 {0}
     104                 :             :         };
     105                 :             : 
     106                 :           2 :         ASSERT(SUCCESS == set_environment_variable("TESTING","true"));
     107                 :             : 
     108                 :           2 :         ASSERT(SUCCESS == run_and_match_templates(cases,NULL,NULL));
     109                 :             : 
     110                 :             :         /* Informational CLI requests must not read a malformed default file */
     111                 :           2 :         ASSERT(SUCCESS == copy_from_origin("tests/templates/0003_003.conf",
     112                 :             :                 "precizer.conf",REQUIRE_SOURCE_EXISTS));
     113                 :           2 :         ASSERT(SUCCESS == run_and_match_templates(cases,NULL,NULL));
     114                 :             : 
     115                 :             :         /* An explicit selection must not cause a conflict or a file access */
     116                 :           2 :         ASSERT(SUCCESS == run_and_match_templates(config_cases,NULL,NULL));
     117                 :           2 :         ASSERT(SUCCESS == copy_from_origin("tests/templates/0003_003.conf",
     118                 :             :                 "0003_003.conf",REQUIRE_SOURCE_EXISTS));
     119                 :           2 :         ASSERT(SUCCESS == run_and_match_templates(config_cases,NULL,NULL));
     120                 :           2 :         call(delete_path_if_present("0003_003.conf"));
     121                 :           2 :         call(delete_path_if_present("precizer.conf"));
     122                 :             : 
     123                 :           2 :         RETURN_STATUS;
     124                 :             : }
     125                 :             : 
     126                 :             : /**
     127                 :             :  * @brief Check that information modes ignore PATH and stop before the main workflow
     128                 :             :  *
     129                 :             :  * Invalid regex values must not be compiled when help, usage, or version is requested
     130                 :             :  *
     131                 :             :  * @return Return status for information modes combined with paths and comparison
     132                 :             :  */
     133                 :           2 : Return test0003_4(void)
     134                 :             : {
     135                 :           2 :         INITTEST;
     136                 :           2 :         const struct argument_test_case cases[] = {
     137                 :             :                 {"--help /definitely/nonexistent/path",COMPLETED,"templates/0003_004.txt",STDERR_MUST_BE_EMPTY},
     138                 :             :                 {"-h /definitely/nonexistent/path",COMPLETED,"templates/0003_004.txt",STDERR_MUST_BE_EMPTY},
     139                 :             :                 {"--usage /definitely/nonexistent/path",COMPLETED,"templates/0003_005.txt",STDERR_MUST_BE_EMPTY},
     140                 :             :                 {"-z /definitely/nonexistent/path",COMPLETED,"templates/0003_005.txt",STDERR_MUST_BE_EMPTY},
     141                 :             :                 {"--version /definitely/nonexistent/path",COMPLETED,"templates/0003_006.txt",STDERR_MUST_BE_EMPTY},
     142                 :             :                 {"--compare --help",COMPLETED,"templates/0003_004.txt",STDERR_MUST_BE_EMPTY},
     143                 :             :                 {"--compare --usage",COMPLETED,"templates/0003_005.txt",STDERR_MUST_BE_EMPTY},
     144                 :             :                 {"--compare -z",COMPLETED,"templates/0003_005.txt",STDERR_MUST_BE_EMPTY},
     145                 :             :                 {"--compare --version",COMPLETED,"templates/0003_006.txt",STDERR_MUST_BE_EMPTY},
     146                 :             :                 {"--help --version",COMPLETED,"templates/0003_004.txt",STDERR_MUST_BE_EMPTY},
     147                 :             :                 {"--path=/definitely/nonexistent/path --help",COMPLETED,"templates/0003_004.txt",STDERR_MUST_BE_EMPTY},
     148                 :             :                 {"--path=/definitely/nonexistent/path --usage",COMPLETED,"templates/0003_005.txt",STDERR_MUST_BE_EMPTY},
     149                 :             :                 {"--path=/definitely/nonexistent/path --version",COMPLETED,"templates/0003_006.txt",STDERR_MUST_BE_EMPTY},
     150                 :             :                 {"--help --ignore='('",COMPLETED,"templates/0003_004_1.txt",STDERR_MUST_BE_EMPTY},
     151                 :             :                 {"--help --include='('",COMPLETED,"templates/0003_004_1.txt",STDERR_MUST_BE_EMPTY},
     152                 :             :                 {"--help --lock-checksum='('",COMPLETED,"templates/0003_004_1.txt",STDERR_MUST_BE_EMPTY},
     153                 :             :                 {"--usage --ignore='('",COMPLETED,"templates/0003_005.txt",STDERR_MUST_BE_EMPTY},
     154                 :             :                 {"--usage --include='('",COMPLETED,"templates/0003_005.txt",STDERR_MUST_BE_EMPTY},
     155                 :             :                 {"--usage --lock-checksum='('",COMPLETED,"templates/0003_005.txt",STDERR_MUST_BE_EMPTY},
     156                 :             :                 {"--version --ignore='('",COMPLETED,"templates/0003_006.txt",STDERR_MUST_BE_EMPTY},
     157                 :             :                 {"--version --include='('",COMPLETED,"templates/0003_006.txt",STDERR_MUST_BE_EMPTY},
     158                 :             :                 {"--version --lock-checksum='('",COMPLETED,"templates/0003_006.txt",STDERR_MUST_BE_EMPTY},
     159                 :             :                 {0}
     160                 :             :         };
     161                 :             : 
     162                 :           2 :         ASSERT(SUCCESS == set_environment_variable("TESTING","true"));
     163                 :             : 
     164                 :           2 :         ASSERT(SUCCESS == run_and_match_templates(cases,NULL,NULL));
     165                 :             : 
     166                 :           2 :         RETURN_STATUS;
     167                 :             : }
     168                 :             : 
     169                 :             : /**
     170                 :             :  * @brief Require named and positional roots to produce the same traversal output
     171                 :             :  *
     172                 :             :  * Run each command first without a default configuration file, then with an
     173                 :             :  * invalid default file, a valid file containing different settings, and a file
     174                 :             :  * without read permission.
     175                 :             :  * All runs must produce the same output and status
     176                 :             :  *
     177                 :             :  * @return Return status for long and short named options and positional roots
     178                 :             :  */
     179                 :           2 : static Return test0003_5(void)
     180                 :             : {
     181                 :           2 :         INITTEST;
     182                 :           2 :         const struct argument_test_case cases[] = {
     183                 :             :                 {"--dry-run --database=dry_run_regular.db tests/fixtures/diffs/diff1",COMPLETED,"templates/0013_001_1.txt",STDERR_MUST_BE_EMPTY},
     184                 :             :                 {"--dry-run --database=dry_run_regular.db --path=tests/fixtures/diffs/diff1/",COMPLETED,"templates/0013_001_1.txt",STDERR_MUST_BE_EMPTY},
     185                 :             :                 {"--dry-run --database=dry_run_regular.db --path tests/fixtures/diffs/diff1/",COMPLETED,"templates/0013_001_1.txt",STDERR_MUST_BE_EMPTY},
     186                 :             :                 {"--dry-run --database=dry_run_regular.db -P tests/fixtures/diffs/diff1/",COMPLETED,"templates/0013_001_1.txt",STDERR_MUST_BE_EMPTY},
     187                 :             :                 {0}
     188                 :             :         };
     189                 :             : 
     190                 :           2 :         ASSERT(SUCCESS == set_environment_variable("TESTING","true"));
     191                 :             : 
     192                 :           2 :         ASSERT(SUCCESS == run_and_match_templates(cases,NULL,NULL));
     193                 :             : 
     194                 :             :         /* Ordinary CLI operations must not read a malformed default file */
     195                 :           2 :         ASSERT(SUCCESS == copy_from_origin("tests/templates/0003_003.conf",
     196                 :             :                 "precizer.conf",REQUIRE_SOURCE_EXISTS));
     197                 :           2 :         ASSERT(SUCCESS == run_and_match_templates(cases,NULL,NULL));
     198                 :             : 
     199                 :             :         /* Valid default settings must not alter or supplement CLI settings */
     200                 :           2 :         ASSERT(SUCCESS == copy_path("tests/templates/0003_005.conf","precizer.conf"));
     201                 :           2 :         ASSERT(SUCCESS == run_and_match_templates(cases,NULL,NULL));
     202                 :             : 
     203                 :             :         /* Read permission on an unused default file must not affect CLI operations */
     204                 :           2 :         ASSERT(SUCCESS == change_mode("precizer.conf",0000));
     205                 :           2 :         ASSERT(SUCCESS == run_and_match_templates(cases,NULL,NULL));
     206                 :           2 :         call(change_mode("precizer.conf",0600));
     207                 :           2 :         call(delete_path_if_present("precizer.conf"));
     208                 :             : 
     209                 :           2 :         RETURN_STATUS;
     210                 :             : }
     211                 :             : 
     212                 :             : /**
     213                 :             :  * @brief Preserve repeated roots and combine named roots with positional roots
     214                 :             :  *
     215                 :             :  * Named options are processed before positional arguments by argp. The full
     216                 :             :  * output must match the equivalent positional root array, including duplicates.
     217                 :             :  * A file repeat must normalize trailing slashes without removing repeated roots
     218                 :             :  *
     219                 :             :  * @return Return status for repeated, mixed, and normalized root spellings
     220                 :             :  */
     221                 :           2 : static Return test0003_6(void)
     222                 :             : {
     223                 :           2 :         INITTEST;
     224                 :           2 :         const struct argument_test_case cases[] = {
     225                 :             :                 {"--dry-run --database=0003_paths.db "
     226                 :             :                  "--path=tests/fixtures/diffs/diff1/1/ --path=tests/fixtures/diffs/diff1/4 "
     227                 :             :                  "--path=tests/fixtures/diffs/diff1/1",COMPLETED,"templates/0003_007.txt",STDERR_MUST_BE_EMPTY},
     228                 :             :                 {"--dry-run --database=0003_paths.db "
     229                 :             :                  "--path=tests/fixtures/diffs/diff1/1/ --path tests/fixtures/diffs/diff1/4/ "
     230                 :             :                  "tests/fixtures/diffs/diff1/1/",COMPLETED,"templates/0003_007.txt",STDERR_MUST_BE_EMPTY},
     231                 :             :                 {"--dry-run --database=0003_paths.db tests/fixtures/diffs/diff1/1/ "
     232                 :             :                  "--path=tests/fixtures/diffs/diff1/1 --path=tests/fixtures/diffs/diff1/4",COMPLETED,"templates/0003_007.txt",STDERR_MUST_BE_EMPTY},
     233                 :             :                 {0}
     234                 :             :         };
     235                 :             : 
     236                 :           2 :         ASSERT(SUCCESS == set_environment_variable("TESTING","true"));
     237                 :             : 
     238                 :           2 :         ASSERT(SUCCESS == run_and_match_templates(cases,NULL,NULL));
     239                 :             : 
     240                 :             :         /* Reuse the dry-run state and full output for the same roots from a file */
     241                 :           2 :         ASSERT(SUCCESS == copy_path("tests/templates/0003_006.conf","0003_006.conf"));
     242                 :           2 :         const struct argument_test_case file_cases[] = {
     243                 :             :                 {"--config=0003_006.conf",COMPLETED,"templates/0003_007.txt",STDERR_MUST_BE_EMPTY},
     244                 :             :                 {0}
     245                 :             :         };
     246                 :           2 :         ASSERT(SUCCESS == run_and_match_templates(file_cases,NULL,NULL));
     247                 :           2 :         call(delete_path_if_present("0003_006.conf"));
     248                 :             : 
     249                 :           2 :         RETURN_STATUS;
     250                 :             : }
     251                 :             : 
     252                 :             : /**
     253                 :             :  * @brief Reject missing roots, missing path values, and missing compare operands
     254                 :             :  *
     255                 :             :  * Empty named and positional roots are invalid. Named roots cannot substitute
     256                 :             :  * for either positional database operand required by compare mode. Missing
     257                 :             :  * scan roots produce a hint for positional paths and --path
     258                 :             :  *
     259                 :             :  * @return Return status for complete stdout and stderr diagnostics
     260                 :             :  */
     261                 :           2 : static Return test0003_7(void)
     262                 :             : {
     263                 :           2 :         INITTEST;
     264                 :           2 :         const struct argument_test_case cases[] = {
     265                 :             :                 {"--dry-run",FAILURE,"templates/0028_006_14.txt","templates/0003_007_1.txt"},
     266                 :             :                 {"--",FAILURE,"templates/0028_006_14.txt","templates/0003_024.txt"},
     267                 :             :                 {"-- tests/fixtures/diffs/diff1",FAILURE,"templates/0028_006_14.txt","templates/0003_024.txt"},
     268                 :             :                 {"--path=tests/fixtures/diffs/diff1 --",FAILURE,"templates/0028_006_14.txt","templates/0003_024.txt"},
     269                 :             :                 {"--compare --",FAILURE,"templates/0028_006_14.txt","templates/0003_024.txt"},
     270                 :             :                 {"--version --",FAILURE,"templates/0028_006_14.txt","templates/0003_024.txt"},
     271                 :             :                 {"--path",FAILURE,"templates/0040_003.txt","templates/0003_008.txt"},
     272                 :             :                 {"--path=",FAILURE,"templates/0040_003.txt","templates/0003_010.txt"},
     273                 :             :                 {"--dry-run ''",FAILURE,"templates/0040_003.txt","templates/0003_010.txt"},
     274                 :             :                 {"--compare --path='' first.db second.db",FAILURE,"templates/0040_003.txt","templates/0003_010.txt"},
     275                 :             :                 {"--compare --path=first.db --path=second.db",FAILURE,"templates/0028_006_14.txt","templates/0003_002.txt"},
     276                 :             :                 {"--compare --path=second.db first.db",FAILURE,"templates/0028_006_14.txt","templates/0003_011.txt"},
     277                 :             :                 {0}
     278                 :             :         };
     279                 :             : 
     280                 :           2 :         ASSERT(SUCCESS == set_environment_variable("TESTING","true"));
     281                 :             : 
     282                 :           2 :         ASSERT(SUCCESS == run_and_match_templates(cases,NULL,NULL));
     283                 :             : 
     284                 :           2 :         RETURN_STATUS;
     285                 :             : }
     286                 :             : 
     287                 :             : /**
     288                 :             :  * @brief Select the default file only for an argument-free invocation
     289                 :             :  *
     290                 :             :  * Missing scan roots produce a hint to add the path key to the configuration
     291                 :             :  * file, whether it was selected automatically or explicitly
     292                 :             :  *
     293                 :             :  * @return Return status for empty, documented, incomplete, and populated files
     294                 :             :  */
     295                 :           2 : static Return test0003_8(void)
     296                 :             : {
     297                 :           2 :         INITTEST;
     298                 :           2 :         const struct argument_test_case empty_file[] = {
     299                 :             :                 {"",COMPLETED,"templates/0003_003.txt","templates/0003_002.txt"},
     300                 :             :                 {"--config=precizer.conf",FAILURE,"templates/0028_006_14.txt","templates/0003_008_1.txt"},
     301                 :             :                 {0}
     302                 :             :         };
     303                 :           2 :         const struct argument_test_case missing_root[] = {
     304                 :             :                 {"",FAILURE,"templates/0028_006_14.txt","templates/0003_008_1.txt"},
     305                 :             :                 {"--config=precizer.conf",FAILURE,"templates/0028_006_14.txt","templates/0003_008_1.txt"},
     306                 :             :                 {0}
     307                 :             :         };
     308                 :           2 :         const struct argument_test_case configured_root[] = {
     309                 :             :                 {"",FAILURE,"templates/0003_012.txt",STDERR_MUST_BE_EMPTY},
     310                 :             :                 {0}
     311                 :             :         };
     312                 :             : 
     313                 :           2 :         ASSERT(SUCCESS == set_environment_variable("TESTING","true"));
     314                 :           2 :         ASSERT(SUCCESS == copy_from_origin("tests/templates/0003_008_1.conf",
     315                 :             :                 "precizer.conf",REQUIRE_SOURCE_EXISTS));
     316                 :           2 :         ASSERT(SUCCESS == run_and_match_templates(empty_file,NULL,NULL));
     317                 :             :         /* The documented default must remain safe until a setting is uncommented */
     318                 :           2 :         ASSERT(SUCCESS == copy_from_origin("precizer.conf","precizer.conf",REQUIRE_SOURCE_EXISTS));
     319                 :           2 :         ASSERT(SUCCESS == run_and_match_templates(empty_file,NULL,NULL));
     320                 :             :         /* A false flag is still a supplied setting, so a scan root is required */
     321                 :           2 :         ASSERT(SUCCESS == copy_from_origin("tests/templates/0003_008_2.conf",
     322                 :             :                 "precizer.conf",REQUIRE_SOURCE_EXISTS));
     323                 :           2 :         ASSERT(SUCCESS == run_and_match_templates(missing_root,NULL,NULL));
     324                 :           2 :         ASSERT(SUCCESS == copy_from_origin("tests/templates/0003_012.conf","precizer.conf",REQUIRE_SOURCE_EXISTS));
     325                 :           2 :         ASSERT(SUCCESS == run_and_match_templates(configured_root,"%CONFIG_PATH%","precizer[.]conf"));
     326                 :             : 
     327                 :           2 :         call(delete_path_if_present("precizer.conf"));
     328                 :           2 :         RETURN_STATUS;
     329                 :             : }
     330                 :             : 
     331                 :             : /**
     332                 :             :  * @brief Read only the last explicit file and resolve its relative roots from the working directory
     333                 :             :  * @return Return status for long, short, attached, separate, and repeated selectors
     334                 :             :  */
     335                 :           2 : static Return test0003_9(void)
     336                 :             : {
     337                 :           2 :         INITTEST;
     338                 :           2 :         const struct argument_test_case cases[] = {
     339                 :             :                 {"--config=tests/templates/0003_012.conf",FAILURE,"templates/0003_012.txt",STDERR_MUST_BE_EMPTY},
     340                 :             :                 {"--config tests/templates/0003_012.conf",FAILURE,"templates/0003_012.txt",STDERR_MUST_BE_EMPTY},
     341                 :             :                 {"-g tests/templates/0003_012.conf",FAILURE,"templates/0003_012.txt",STDERR_MUST_BE_EMPTY},
     342                 :             :                 {"-gtests/templates/0003_012.conf",FAILURE,"templates/0003_012.txt",STDERR_MUST_BE_EMPTY},
     343                 :             :                 {"--config=0003_absent.conf -gtests/templates/0003_012.conf",FAILURE,"templates/0003_012.txt",STDERR_MUST_BE_EMPTY},
     344                 :             :                 {"-gprecizer.conf --config=tests/templates/0003_012.conf",FAILURE,"templates/0003_012.txt",STDERR_MUST_BE_EMPTY},
     345                 :             :                 {"--config -- -g tests/templates/0003_012.conf",FAILURE,"templates/0003_012.txt",STDERR_MUST_BE_EMPTY},
     346                 :             :                 {0}
     347                 :             :         };
     348                 :             : 
     349                 :           2 :         ASSERT(SUCCESS == set_environment_variable("TESTING","true"));
     350                 :           2 :         ASSERT(SUCCESS == copy_from_origin("tests/templates/0003_003.conf",
     351                 :             :                 "precizer.conf",REQUIRE_SOURCE_EXISTS));
     352                 :           2 :         ASSERT(SUCCESS == copy_from_origin("tests/templates/0003_012.conf","tests/templates/0003_012.conf",REQUIRE_SOURCE_EXISTS));
     353                 :             :         /* A root relative to the config directory would incorrectly find this directory */
     354                 :           2 :         ASSERT(SUCCESS == create_directory("tests/templates/0003_missing_root"));
     355                 :           2 :         ASSERT(SUCCESS == run_and_match_templates(cases,"%CONFIG_PATH%","tests/templates/0003_012[.]conf"));
     356                 :             : 
     357                 :           2 :         call(delete_path_if_present("tests/templates/0003_missing_root"));
     358                 :           2 :         call(delete_path_if_present("tests/templates/0003_012.conf"));
     359                 :           2 :         call(delete_path_if_present("precizer.conf"));
     360                 :           2 :         RETURN_STATUS;
     361                 :             : }
     362                 :             : 
     363                 :             : /**
     364                 :             :  * @brief Accept option-like values and configuration filenames containing spaces
     365                 :             :  *
     366                 :             :  * Config selector names passed as --path or -P values remain scan roots.
     367                 :             :  * Missing directories must produce path diagnostics, not source-selection errors
     368                 :             :  *
     369                 :             :  * @return Return status for literal option-like values and quoted paths
     370                 :             :  */
     371                 :           2 : static Return test0003_10(void)
     372                 :             : {
     373                 :           2 :         INITTEST;
     374                 :           2 :         const struct argument_test_case delimiter_name[] = {
     375                 :             :                 {"--config=--",FAILURE,"templates/0003_012.txt",STDERR_MUST_BE_EMPTY},
     376                 :             :                 {"-g--",FAILURE,"templates/0003_012.txt",STDERR_MUST_BE_EMPTY},
     377                 :             :                 {0}
     378                 :             :         };
     379                 :           2 :         const struct argument_test_case relative_name[] = {
     380                 :             :                 {"--config ./--",FAILURE,"templates/0003_012.txt",STDERR_MUST_BE_EMPTY},
     381                 :             :                 {0}
     382                 :             :         };
     383                 :           2 :         const struct argument_test_case option_name[] = {
     384                 :             :                 {"--config --help",FAILURE,"templates/0003_012.txt",STDERR_MUST_BE_EMPTY},
     385                 :             :                 {0}
     386                 :             :         };
     387                 :           2 :         const struct argument_test_case spaced_name[] = {
     388                 :             :                 {"-g '0003 config.conf'",FAILURE,"templates/0003_012.txt",STDERR_MUST_BE_EMPTY},
     389                 :             :                 {0}
     390                 :             :         };
     391                 :           2 :         const struct argument_test_case root_values[] = {
     392                 :             :                 {"--path --config --path -g",FAILURE,"templates/0003_010_1.txt",STDERR_MUST_BE_EMPTY},
     393                 :             :                 {"-P --config -P -g",FAILURE,"templates/0003_010_1.txt",STDERR_MUST_BE_EMPTY},
     394                 :             :                 {0}
     395                 :             :         };
     396                 :             : 
     397                 :           2 :         ASSERT(SUCCESS == set_environment_variable("TESTING","true"));
     398                 :           2 :         ASSERT(SUCCESS == copy_from_origin("tests/templates/0003_012.conf","--",REQUIRE_SOURCE_EXISTS));
     399                 :           2 :         ASSERT(SUCCESS == run_and_match_templates(delimiter_name,"%CONFIG_PATH%","--"));
     400                 :           2 :         ASSERT(SUCCESS == run_and_match_templates(relative_name,"%CONFIG_PATH%","[.]/--"));
     401                 :           2 :         ASSERT(SUCCESS == copy_from_origin("tests/templates/0003_012.conf","--help",REQUIRE_SOURCE_EXISTS));
     402                 :           2 :         ASSERT(SUCCESS == run_and_match_templates(option_name,"%CONFIG_PATH%","--help"));
     403                 :           2 :         ASSERT(SUCCESS == copy_from_origin("tests/templates/0003_012.conf","0003 config.conf",REQUIRE_SOURCE_EXISTS));
     404                 :           2 :         ASSERT(SUCCESS == run_and_match_templates(spaced_name,"%CONFIG_PATH%","0003 config[.]conf"));
     405                 :           2 :         ASSERT(SUCCESS == run_and_match_templates(root_values,NULL,NULL));
     406                 :             : 
     407                 :           2 :         call(delete_path_if_present("0003 config.conf"));
     408                 :           2 :         call(delete_path_if_present("--help"));
     409                 :           2 :         call(delete_path_if_present("--"));
     410                 :           2 :         RETURN_STATUS;
     411                 :             : }
     412                 :             : 
     413                 :             : /**
     414                 :             :  * @brief Reject explicit file selection combined with other CLI arguments before opening the file
     415                 :             :  *
     416                 :             :  * Check all selector forms, both argument orders, repeated selections, and a
     417                 :             :  * short-option group. Missing, malformed, and unreadable files must produce
     418                 :             :  * the same source-conflict diagnostic without attempting to read settings
     419                 :             :  *
     420                 :             :  * @return Return status for operational and positional conflicts
     421                 :             :  */
     422                 :           2 : static Return test0003_11(void)
     423                 :             : {
     424                 :           2 :         INITTEST;
     425                 :           2 :         const struct argument_test_case cases[] = {
     426                 :             :                 {"--config=0003_011.conf --path=0003_missing_root",FAILURE,"templates/0003_013.txt","templates/0003_014.txt"},
     427                 :             :                 {"--path=0003_missing_root --config=0003_011.conf",FAILURE,"templates/0003_013.txt","templates/0003_014.txt"},
     428                 :             :                 {"--config=0003_011.conf 0003_missing_root",FAILURE,"templates/0003_013.txt","templates/0003_014.txt"},
     429                 :             :                 {"0003_missing_root --config=0003_011.conf",FAILURE,"templates/0003_013.txt","templates/0003_014.txt"},
     430                 :             :                 {"--config 0003_011.conf --path=0003_missing_root",FAILURE,"templates/0003_013.txt","templates/0003_014.txt"},
     431                 :             :                 {"--path=0003_missing_root --config 0003_011.conf",FAILURE,"templates/0003_013.txt","templates/0003_014.txt"},
     432                 :             :                 {"--config 0003_011.conf 0003_missing_root",FAILURE,"templates/0003_013.txt","templates/0003_014.txt"},
     433                 :             :                 {"0003_missing_root --config 0003_011.conf",FAILURE,"templates/0003_013.txt","templates/0003_014.txt"},
     434                 :             :                 {"-g 0003_011.conf --path=0003_missing_root",FAILURE,"templates/0003_013.txt","templates/0003_014.txt"},
     435                 :             :                 {"--path=0003_missing_root -g 0003_011.conf",FAILURE,"templates/0003_013.txt","templates/0003_014.txt"},
     436                 :             :                 {"-g 0003_011.conf 0003_missing_root",FAILURE,"templates/0003_013.txt","templates/0003_014.txt"},
     437                 :             :                 {"0003_missing_root -g 0003_011.conf",FAILURE,"templates/0003_013.txt","templates/0003_014.txt"},
     438                 :             :                 {"-g0003_011.conf --path=0003_missing_root",FAILURE,"templates/0003_013.txt","templates/0003_014.txt"},
     439                 :             :                 {"--path=0003_missing_root -g0003_011.conf",FAILURE,"templates/0003_013.txt","templates/0003_014.txt"},
     440                 :             :                 {"-g0003_011.conf 0003_missing_root",FAILURE,"templates/0003_013.txt","templates/0003_014.txt"},
     441                 :             :                 {"0003_missing_root -g0003_011.conf",FAILURE,"templates/0003_013.txt","templates/0003_014.txt"},
     442                 :             :                 {"-vg0003_011.conf",FAILURE,"templates/0003_013.txt","templates/0003_014.txt"},
     443                 :             :                 {"--config=0003_absent.conf --path=0003_missing_root -g0003_011.conf",FAILURE,"templates/0003_013.txt","templates/0003_014.txt"},
     444                 :             :                 {"0003_missing_root -g0003_absent.conf --config=0003_011.conf",FAILURE,"templates/0003_013.txt","templates/0003_014.txt"},
     445                 :             :                 {0}
     446                 :             :         };
     447                 :             : 
     448                 :           2 :         ASSERT(SUCCESS == set_environment_variable("TESTING","true"));
     449                 :           2 :         ASSERT(SUCCESS == run_and_match_templates(cases,NULL,NULL));
     450                 :             : 
     451                 :             :         /* Invalid settings must not replace the conflict with a configuration error */
     452                 :           2 :         ASSERT(SUCCESS == copy_path("tests/templates/0003_011.conf","0003_011.conf"));
     453                 :           2 :         ASSERT(SUCCESS == run_and_match_templates(cases,NULL,NULL));
     454                 :             :         /* File permissions must not replace the source conflict with an open error */
     455                 :           2 :         ASSERT(SUCCESS == change_mode("0003_011.conf",0000));
     456                 :           2 :         ASSERT(SUCCESS == run_and_match_templates(cases,NULL,NULL));
     457                 :           2 :         call(change_mode("0003_011.conf",0600));
     458                 :           2 :         call(delete_path_if_present("0003_011.conf"));
     459                 :             : 
     460                 :           2 :         RETURN_STATUS;
     461                 :             : }
     462                 :             : 
     463                 :             : /**
     464                 :             :  * @brief Defer invalid CLI diagnostics to the main parser without reading a file
     465                 :             :  *
     466                 :             :  * Preserve the first main-pass error and any help or silent behavior preceding it
     467                 :             :  *
     468                 :             :  * @return Return status for missing values, unknown options, forbidden option values, and argp-marked --
     469                 :             :  */
     470                 :           2 : static Return test0003_12(void)
     471                 :             : {
     472                 :           2 :         INITTEST;
     473                 :           2 :         const struct argument_test_case cases[] = {
     474                 :             :                 {"--config",FAILURE,"templates/0040_003.txt","templates/0003_015.txt"},
     475                 :             :                 {"-g",FAILURE,"templates/0040_003.txt","templates/0003_016.txt"},
     476                 :             :                 {"--config=0003_absent.conf --path",FAILURE,"templates/0040_003.txt","templates/0003_008.txt"},
     477                 :             :                 {"--config=0003_absent.conf --unknown",FAILURE,"templates/0040_003.txt","templates/0003_017.txt"},
     478                 :             :                 {"--unknown --config=0003_absent.conf",FAILURE,"templates/0040_003.txt","templates/0003_017.txt"},
     479                 :             :                 {"--maxdepth=bad --unknown",FAILURE,"templates/0040_003.txt","templates/0018_005_2.txt"},
     480                 :             :                 {"--help --unknown",FAILURE,"templates/0003_012_1.txt","templates/0003_017.txt"},
     481                 :             :                 {"--silent --unknown",FAILURE,"templates/0045_033.txt","templates/0003_017.txt"},
     482                 :             :                 {"--version --config=0003_absent.conf --unknown",FAILURE,"templates/0040_003.txt","templates/0003_017.txt"},
     483                 :             :                 {"--config=0003_absent.conf --version --color=invalid-value",FAILURE,"templates/0040_003.txt","templates/0040_004.txt"},
     484                 :             :                 {"--config=0003_absent.conf --progress=no",FAILURE,"templates/0040_003.txt","templates/0003_018.txt"},
     485                 :             :                 {"--",FAILURE,"templates/0028_006_14.txt","templates/0003_024.txt"},
     486                 :             :                 {"-- tests/fixtures/diffs/diff1",FAILURE,"templates/0028_006_14.txt","templates/0003_024.txt"},
     487                 :             :                 {"--path=tests/fixtures/diffs/diff1 --",FAILURE,"templates/0028_006_14.txt","templates/0003_024.txt"},
     488                 :             :                 {"--compare --",FAILURE,"templates/0028_006_14.txt","templates/0003_024.txt"},
     489                 :             :                 {"--version --",FAILURE,"templates/0028_006_14.txt","templates/0003_024.txt"},
     490                 :             :                 {"--config --",FAILURE,"templates/0028_006_14.txt","templates/0003_024.txt"},
     491                 :             :                 {"-g --",FAILURE,"templates/0028_006_14.txt","templates/0003_024.txt"},
     492                 :             :                 {"--config=0003_absent.conf --",FAILURE,"templates/0028_006_14.txt","templates/0003_024.txt"},
     493                 :             :                 {"--config -- --",FAILURE,"templates/0028_006_14.txt","templates/0003_024.txt"},
     494                 :             :                 {"-g -- --",FAILURE,"templates/0028_006_14.txt","templates/0003_024.txt"},
     495                 :             :                 {"--config=-- --",FAILURE,"templates/0028_006_14.txt","templates/0003_024.txt"},
     496                 :             :                 {"-g-- --",FAILURE,"templates/0028_006_14.txt","templates/0003_024.txt"},
     497                 :             :                 {0}
     498                 :             :         };
     499                 :             : 
     500                 :           2 :         ASSERT(SUCCESS == set_environment_variable("TESTING","true"));
     501                 :           2 :         ASSERT(SUCCESS == copy_from_origin("tests/templates/0003_003.conf",
     502                 :             :                 "precizer.conf",REQUIRE_SOURCE_EXISTS));
     503                 :           2 :         ASSERT(SUCCESS == run_and_match_templates(cases,NULL,NULL));
     504                 :           2 :         call(delete_path_if_present("precizer.conf"));
     505                 :           2 :         RETURN_STATUS;
     506                 :             : }
     507                 :             : 
     508                 :             : /**
     509                 :             :  * @brief Stop startup on configuration open, read, and parsing errors without fallback
     510                 :             :  *
     511                 :             :  * A file without read permission must fail for default and explicit selection.
     512                 :             :  * Information requests must ignore the same inaccessible file
     513                 :             :  *
     514                 :             :  * @return Return status for default and explicitly selected file errors
     515                 :             :  */
     516                 :           2 : static Return test0003_13(void)
     517                 :             : {
     518                 :           2 :         INITTEST;
     519                 :           2 :         const struct argument_test_case missing_file[] = {
     520                 :             :                 {"--config=0003_absent.conf",FAILURE,"templates/0003_013.txt","templates/0003_019.txt"},
     521                 :             :                 {"--config=",FAILURE,"templates/0003_013.txt","templates/0003_020.txt"},
     522                 :             :                 {0}
     523                 :             :         };
     524                 :           2 :         const struct argument_test_case invalid_file[] = {
     525                 :             :                 {"",FAILURE,"templates/0003_013.txt","templates/0003_021.txt"},
     526                 :             :                 {"--config=precizer.conf",FAILURE,"templates/0003_013.txt","templates/0003_021.txt"},
     527                 :             :                 {0}
     528                 :             :         };
     529                 :             : 
     530                 :           2 :         ASSERT(SUCCESS == set_environment_variable("TESTING","true"));
     531                 :           2 :         ASSERT(SUCCESS == copy_from_origin("tests/templates/0003_012.conf","precizer.conf",REQUIRE_SOURCE_EXISTS));
     532                 :           2 :         ASSERT(SUCCESS == run_and_match_templates(missing_file,NULL,NULL));
     533                 :           2 :         ASSERT(SUCCESS == copy_from_origin("tests/templates/0003_003.conf",
     534                 :             :                 "precizer.conf",REQUIRE_SOURCE_EXISTS));
     535                 :           2 :         ASSERT(SUCCESS == run_and_match_templates(invalid_file,NULL,NULL));
     536                 :           2 :         call(delete_path_if_present("precizer.conf"));
     537                 :             : 
     538                 :             :         /* A directory opens successfully but causes a read error when inih reads it */
     539                 :           2 :         const struct argument_test_case unreadable_file[] = {
     540                 :             :                 {"",FAILURE,"templates/0003_013.txt","templates/0003_022.txt"},
     541                 :             :                 {"--config=precizer.conf",FAILURE,"templates/0003_013.txt","templates/0003_022.txt"},
     542                 :             :                 {"--version",COMPLETED,"templates/0003_006.txt",STDERR_MUST_BE_EMPTY},
     543                 :             :                 {0}
     544                 :             :         };
     545                 :           2 :         ASSERT(SUCCESS == create_directory("precizer.conf"));
     546                 :           2 :         ASSERT(SUCCESS == run_and_match_templates(unreadable_file,NULL,NULL));
     547                 :           2 :         call(delete_path_if_present("precizer.conf"));
     548                 :             : 
     549                 :             :         /* Permission denial is an open error, unlike a directory's read error */
     550                 :           2 :         const struct argument_test_case denied_file[] = {
     551                 :             :                 {"",FAILURE,"templates/0003_013.txt","templates/0003_013_1.txt"},
     552                 :             :                 {"--config=precizer.conf",FAILURE,"templates/0003_013.txt","templates/0003_013_1.txt"},
     553                 :             :                 {"--version",COMPLETED,"templates/0003_006.txt",STDERR_MUST_BE_EMPTY},
     554                 :             :                 {"--config=precizer.conf --version",COMPLETED,"templates/0003_006.txt",STDERR_MUST_BE_EMPTY},
     555                 :             :                 {0}
     556                 :             :         };
     557                 :           2 :         ASSERT(SUCCESS == copy_path("tests/templates/0003_005.conf","precizer.conf"));
     558                 :           2 :         ASSERT(SUCCESS == change_mode("precizer.conf",0000));
     559                 :           2 :         ASSERT(SUCCESS == run_and_match_templates(denied_file,NULL,NULL));
     560                 :           2 :         call(change_mode("precizer.conf",0600));
     561                 :           2 :         call(delete_path_if_present("precizer.conf"));
     562                 :             : 
     563                 :           2 :         RETURN_STATUS;
     564                 :             : }
     565                 :             : 
     566                 :             : /**
     567                 :             :  * @brief Include the selected file path in verbose configuration diagnostics
     568                 :             :  * @return Return status for the complete verbose startup output
     569                 :             :  */
     570                 :           2 : static Return test0003_14(void)
     571                 :             : {
     572                 :           2 :         INITTEST;
     573                 :           2 :         const struct argument_test_case cases[] = {
     574                 :             :                 {"--config=tests/templates/0003_014.conf",FAILURE,"templates/0003_023.txt",STDERR_MUST_BE_EMPTY},
     575                 :             :                 {0}
     576                 :             :         };
     577                 :             : 
     578                 :           2 :         ASSERT(SUCCESS == set_environment_variable("TESTING","true"));
     579                 :           2 :         ASSERT(SUCCESS == copy_from_origin("tests/templates/0003_014.conf","tests/templates/0003_014.conf",REQUIRE_SOURCE_EXISTS));
     580                 :           2 :         ASSERT(SUCCESS == run_and_match_templates(cases,NULL,NULL));
     581                 :           2 :         call(delete_path_if_present("tests/templates/0003_014.conf"));
     582                 :           2 :         RETURN_STATUS;
     583                 :             : }
     584                 :             : 
     585                 :             : /**
     586                 :             :  * @brief Reject invalid regex values identically through CLI and configuration
     587                 :             :  *
     588                 :             :  * Each pattern is compiled during normal startup after argument parsing.
     589                 :             :  * Full output matching verifies the PCRE2 error and that no traversal starts
     590                 :             :  *
     591                 :             :  * @return Return status for ignore, include, and lock-checksum compilation errors
     592                 :             :  */
     593                 :           2 : static Return test0003_15(void)
     594                 :             : {
     595                 :           2 :         INITTEST;
     596                 :           2 :         const struct argument_test_case cases[] = {
     597                 :             :                 {"--ignore='(' tests/fixtures/diffs/diff1",FAILURE,"templates/0003_015_1.txt",STDERR_MUST_BE_EMPTY},
     598                 :             :                 {"--config=0003_015_1.conf",FAILURE,"templates/0003_015_1.txt",STDERR_MUST_BE_EMPTY},
     599                 :             :                 {"--include='(' tests/fixtures/diffs/diff1",FAILURE,"templates/0003_015_2.txt",STDERR_MUST_BE_EMPTY},
     600                 :             :                 {"--config=0003_015_2.conf",FAILURE,"templates/0003_015_2.txt",STDERR_MUST_BE_EMPTY},
     601                 :             :                 {"--lock-checksum='(' tests/fixtures/diffs/diff1",FAILURE,"templates/0003_015_3.txt",STDERR_MUST_BE_EMPTY},
     602                 :             :                 {"--config=0003_015_3.conf",FAILURE,"templates/0003_015_3.txt",STDERR_MUST_BE_EMPTY},
     603                 :             :                 {0}
     604                 :             :         };
     605                 :             : 
     606                 :           2 :         ASSERT(SUCCESS == set_environment_variable("TESTING","true"));
     607                 :           2 :         ASSERT(SUCCESS == copy_path("tests/templates/0003_015_1.conf","0003_015_1.conf"));
     608                 :           2 :         ASSERT(SUCCESS == copy_path("tests/templates/0003_015_2.conf","0003_015_2.conf"));
     609                 :           2 :         ASSERT(SUCCESS == copy_path("tests/templates/0003_015_3.conf","0003_015_3.conf"));
     610                 :           2 :         ASSERT(SUCCESS == run_and_match_templates(cases,NULL,NULL));
     611                 :           2 :         call(delete_path_if_present("0003_015_1.conf"));
     612                 :           2 :         call(delete_path_if_present("0003_015_2.conf"));
     613                 :           2 :         call(delete_path_if_present("0003_015_3.conf"));
     614                 :             : 
     615                 :           2 :         RETURN_STATUS;
     616                 :             : }
     617                 :             : 
     618                 :           2 : Return test0003(void)
     619                 :             : {
     620                 :           2 :         INITTEST;
     621                 :             : 
     622                 :           2 :         TEST(test0003_1,"Comply default DB name to \"hostname.db\" template");
     623                 :           2 :         TEST(test0003_2,"Running the application with no arguments at all");
     624                 :           2 :         TEST(test0003_3,"Information modes without PATH return success");
     625                 :           2 :         TEST(test0003_4,"Information modes ignore PATH and stop early");
     626                 :           2 :         TEST(test0003_5,"Named roots match positional traversal");
     627                 :           2 :         TEST(test0003_6,"Repeated and positional roots are combined without removing duplicates");
     628                 :           2 :         TEST(test0003_7,"Missing or empty roots and missing compare operands are rejected");
     629                 :           2 :         TEST(test0003_8,"Default configuration distinguishes empty files from incomplete settings");
     630                 :           2 :         TEST(test0003_9,"Explicit configuration forms select only the last file");
     631                 :           2 :         TEST(test0003_10,"Option-like values and config filenames with spaces are accepted");
     632                 :           2 :         TEST(test0003_11,"Explicit configuration conflicts with other CLI arguments before reading");
     633                 :           2 :         TEST(test0003_12,"Invalid CLI arguments and argp-marked -- are reported without reading configuration");
     634                 :           2 :         TEST(test0003_13,"Configuration errors stop startup without fallback");
     635                 :           2 :         TEST(test0003_14,"Verbose diagnostics identify the selected configuration file");
     636                 :           2 :         TEST(test0003_15,"Invalid regex values produce the same CLI and configuration errors");
     637                 :             : 
     638                 :           2 :         RETURN_STATUS;
     639                 :             : }
        

Generated by: LCOV version 2.0-1