LCOV - code coverage report
Current view: top level - tests - catch_config_file.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 2177 2189 99.5 %
Date: 2024-10-05 13:34:54 Functions: 16 16 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : // Copyright (c) 2006-2024  Made to Order Software Corp.  All Rights Reserved
       2             : //
       3             : // https://snapwebsites.org/project/advgetopt
       4             : // contact@m2osw.com
       5             : //
       6             : // This program is free software; you can redistribute it and/or modify
       7             : // it under the terms of the GNU General Public License as published by
       8             : // the Free Software Foundation; either version 2 of the License, or
       9             : // (at your option) any later version.
      10             : //
      11             : // This program is distributed in the hope that it will be useful,
      12             : // but WITHOUT ANY WARRANTY; without even the implied warranty of
      13             : // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      14             : // GNU General Public License for more details.
      15             : //
      16             : // You should have received a copy of the GNU General Public License along
      17             : // with this program; if not, write to the Free Software Foundation, Inc.,
      18             : // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
      19             : 
      20             : // advgetopt
      21             : //
      22             : #include    <advgetopt/conf_file.h>
      23             : 
      24             : #include    <advgetopt/exception.h>
      25             : 
      26             : 
      27             : // self
      28             : //
      29             : #include    "catch_main.h"
      30             : 
      31             : 
      32             : // libutf8
      33             : //
      34             : #include    <libutf8/libutf8.h>
      35             : 
      36             : 
      37             : // snapdev
      38             : //
      39             : #include    <snapdev/safe_setenv.h>
      40             : #include    <snapdev/tokenize_string.h>
      41             : 
      42             : 
      43             : // C++
      44             : //
      45             : #include    <fstream>
      46             : #include    <iomanip>
      47             : 
      48             : 
      49             : // C
      50             : //
      51             : #include    <unistd.h>
      52             : 
      53             : 
      54             : // last include
      55             : //
      56             : #include    <snapdev/poison.h>
      57             : 
      58             : 
      59             : 
      60             : 
      61             : 
      62             : 
      63             : 
      64           1 : CATCH_TEST_CASE("configuration_spaces", "[config][getopt][valid]")
      65             : {
      66           1 :     CATCH_START_SECTION("configuration_spaces: verify configuration spaces")
      67             :     {
      68     1114113 :         for(int c(0); c < 0x110000; ++c)
      69             :         {
      70     1114112 :             if(c == '\r'
      71     1114111 :             || c == '\n')
      72             :             {
      73           2 :                 CATCH_REQUIRE_FALSE(advgetopt::iswspace(c));
      74           2 :             }
      75     1114110 :             else if(std::iswspace(c))
      76             :             {
      77           4 :                 CATCH_REQUIRE(advgetopt::iswspace(c));
      78             :             }
      79             :             else
      80             :             {
      81     1114106 :                 CATCH_REQUIRE_FALSE(advgetopt::iswspace(c));
      82             :             }
      83             :         }
      84             :     }
      85           1 :     CATCH_END_SECTION()
      86           1 : }
      87             : 
      88             : 
      89           2 : CATCH_TEST_CASE("configuration_setup", "[config][getopt][valid]")
      90             : {
      91           2 :     CATCH_START_SECTION("configuration_setup: check all setups")
      92             :     {
      93             :         // 5 * 6 * 16 * 8 * 16 = 61440
      94           6 :         for(int count(0); count < 5; ++count)
      95             :         {
      96           5 :             int const id(rand());
      97          10 :             std::string const name("setup-file-" + std::to_string(id));
      98             : 
      99           5 :             SNAP_CATCH2_NAMESPACE::init_tmp_dir("setup", name);
     100             : 
     101             :             {
     102           5 :                 std::ofstream config_file;
     103           5 :                 config_file.open(SNAP_CATCH2_NAMESPACE::g_config_filename, std::ios_base::out | std::ios_base::binary | std::ios_base::trunc);
     104           5 :                 CATCH_REQUIRE(config_file.good());
     105             :                 config_file <<
     106             :                     "# Auto-generated\n"
     107           5 :                     "param=optional\n"
     108             :                 ;
     109           5 :             }
     110             : 
     111          35 :             for(int lc(static_cast<int>(advgetopt::line_continuation_t::line_continuation_single_line));
     112          35 :                 lc <= static_cast<int>(advgetopt::line_continuation_t::line_continuation_semicolon);
     113             :                 ++lc)
     114             :             {
     115         510 :                 for(advgetopt::assignment_operator_t ao(0);
     116         510 :                     ao <= advgetopt::ASSIGNMENT_OPERATOR_MASK;
     117             :                     ++ao)
     118             :                 {
     119        3840 :                     for(advgetopt::comment_t c(0);
     120        3840 :                         c < advgetopt::COMMENT_MASK;
     121             :                         ++c)
     122             :                     {
     123       53760 :                         for(advgetopt::section_operator_t so(0);
     124       53760 :                             so < advgetopt::SECTION_OPERATOR_MASK;
     125             :                             ++so)
     126             :                         {
     127       50400 :                             advgetopt::conf_file_setup setup(SNAP_CATCH2_NAMESPACE::g_config_filename
     128             :                                                 , static_cast<advgetopt::line_continuation_t>(lc)
     129             :                                                 , ao
     130             :                                                 , c
     131       50400 :                                                 , so);
     132             : 
     133       50400 :                             advgetopt::assignment_operator_t real_ao(ao == 0 ? advgetopt::ASSIGNMENT_OPERATOR_EQUAL : ao);
     134             : 
     135       50400 :                             CATCH_REQUIRE(setup.get_original_filename() == SNAP_CATCH2_NAMESPACE::g_config_filename);
     136             : 
     137       50400 :                             CATCH_REQUIRE(setup.is_valid());
     138       50400 :                             std::unique_ptr<char, decltype(&::free)> fn(realpath(SNAP_CATCH2_NAMESPACE::g_config_filename.c_str(), nullptr), &::free);
     139       50400 :                             CATCH_REQUIRE(setup.get_filename() == fn.get());
     140       50400 :                             CATCH_REQUIRE(setup.get_line_continuation() == static_cast<advgetopt::line_continuation_t>(lc));
     141       50400 :                             CATCH_REQUIRE(setup.get_assignment_operator() == real_ao);
     142       50400 :                             CATCH_REQUIRE(setup.get_comment() == c);
     143       50400 :                             CATCH_REQUIRE(setup.get_section_operator() == so);
     144             : 
     145       50400 :                             std::string const url(setup.get_config_url());
     146             : //std::cerr << "+++ " << lc << " / " << ao << " / " << c << " / " << so << " URL [" << url << "]\n";
     147       50400 :                             CATCH_REQUIRE(url.substr(0, 8) == "file:///");
     148             : 
     149       50400 :                             CATCH_REQUIRE(url.substr(7, strlen(fn.get())) == fn.get());
     150             : 
     151       50400 :                             std::string::size_type const qm_pos(url.find('?'));
     152       50400 :                             if(qm_pos == std::string::npos)
     153             :                             {
     154             :                                 // must have the defaults in this case
     155             :                                 //
     156           0 :                                 CATCH_REQUIRE(static_cast<advgetopt::line_continuation_t>(lc) == advgetopt::line_continuation_t::line_continuation_unix);
     157           0 :                                 CATCH_REQUIRE(real_ao == advgetopt::ASSIGNMENT_OPERATOR_EQUAL);
     158           0 :                                 CATCH_REQUIRE(c  == (advgetopt::COMMENT_INI | advgetopt::COMMENT_SHELL));
     159           0 :                                 CATCH_REQUIRE(so == advgetopt::SECTION_OPERATOR_INI_FILE);
     160             :                             }
     161             :                             else
     162             :                             {
     163       50400 :                                 std::string const qs(url.substr(qm_pos + 1));
     164             : 
     165       50400 :                                 std::vector<std::string> strings;
     166       50400 :                                 snapdev::tokenize_string(strings, qs, "&");
     167             : 
     168       50400 :                                 bool def_lc(true);
     169       50400 :                                 bool def_ao(true);
     170       50400 :                                 bool def_c(true);
     171       50400 :                                 bool def_so(true);
     172             : 
     173      230580 :                                 for(auto s : strings)
     174             :                                 {
     175      180180 :                                     std::string::size_type const eq_pos(s.find('='));
     176      180180 :                                     CATCH_REQUIRE(eq_pos != std::string::npos);
     177             : 
     178      180180 :                                     std::string const var_name(s.substr(0, eq_pos));
     179      180180 :                                     std::string const var_value(s.substr(eq_pos + 1));
     180             : 
     181      180180 :                                     if(var_name == "line-continuation")
     182             :                                     {
     183       42000 :                                         def_lc = false;
     184       42000 :                                         switch(static_cast<advgetopt::line_continuation_t>(lc))
     185             :                                         {
     186        8400 :                                         case advgetopt::line_continuation_t::line_continuation_single_line:
     187        8400 :                                             CATCH_REQUIRE(var_value == "single-line");
     188        8400 :                                             break;
     189             : 
     190        8400 :                                         case advgetopt::line_continuation_t::line_continuation_rfc_822:
     191        8400 :                                             CATCH_REQUIRE(var_value == "rfc-822");
     192        8400 :                                             break;
     193             : 
     194        8400 :                                         case advgetopt::line_continuation_t::line_continuation_msdos:
     195        8400 :                                             CATCH_REQUIRE(var_value == "msdos");
     196        8400 :                                             break;
     197             : 
     198           0 :                                         case advgetopt::line_continuation_t::line_continuation_unix:
     199           0 :                                             CATCH_REQUIRE(var_value == "unix");
     200           0 :                                             break;
     201             : 
     202        8400 :                                         case advgetopt::line_continuation_t::line_continuation_fortran:
     203        8400 :                                             CATCH_REQUIRE(var_value == "fortran");
     204        8400 :                                             break;
     205             : 
     206        8400 :                                         case advgetopt::line_continuation_t::line_continuation_semicolon:
     207        8400 :                                             CATCH_REQUIRE(var_value == "semi-colon");
     208        8400 :                                             break;
     209             : 
     210           0 :                                         default:
     211           0 :                                             CATCH_REQUIRE(("unknown_var_value for \"line-continuation\":" + var_value) == std::string());
     212           0 :                                             break;
     213             : 
     214             :                                         }
     215             :                                     }
     216      138180 :                                     else if(var_name == "assignment-operator")
     217             :                                     {
     218       44100 :                                         def_ao = false;
     219       44100 :                                         std::vector<std::string> operators;
     220       44100 :                                         snapdev::tokenize_string(operators, var_value, ",");
     221             : 
     222       44100 :                                         if((real_ao & advgetopt::ASSIGNMENT_OPERATOR_EQUAL) != 0)
     223             :                                         {
     224       22050 :                                             auto it(std::find(operators.begin(), operators.end(), "equal"));
     225       22050 :                                             CATCH_REQUIRE(it != operators.end());
     226       22050 :                                             operators.erase(it);
     227             :                                         }
     228       44100 :                                         if((real_ao & advgetopt::ASSIGNMENT_OPERATOR_COLON) != 0)
     229             :                                         {
     230       25200 :                                             auto it(std::find(operators.begin(), operators.end(), "colon"));
     231       25200 :                                             CATCH_REQUIRE(it != operators.end());
     232       25200 :                                             operators.erase(it);
     233             :                                         }
     234       44100 :                                         if((real_ao & advgetopt::ASSIGNMENT_OPERATOR_SPACE) != 0)
     235             :                                         {
     236       25200 :                                             auto it(std::find(operators.begin(), operators.end(), "space"));
     237       25200 :                                             CATCH_REQUIRE(it != operators.end());
     238       25200 :                                             operators.erase(it);
     239             :                                         }
     240       44100 :                                         if((real_ao & advgetopt::ASSIGNMENT_OPERATOR_EXTENDED) != 0)
     241             :                                         {
     242       25200 :                                             auto it(std::find(operators.begin(), operators.end(), "extended"));
     243       25200 :                                             CATCH_REQUIRE(it != operators.end());
     244       25200 :                                             operators.erase(it);
     245             :                                         }
     246             : 
     247       44100 :                                         CATCH_REQUIRE(operators.empty());
     248       44100 :                                     }
     249       94080 :                                     else if(var_name == "comment")
     250             :                                     {
     251       50400 :                                         def_c = false;
     252       50400 :                                         std::vector<std::string> comments;
     253       50400 :                                         snapdev::tokenize_string(comments, var_value, ",");
     254             : 
     255       50400 :                                         if((c & advgetopt::COMMENT_INI) != 0)
     256             :                                         {
     257       21600 :                                             auto it(std::find(comments.begin(), comments.end(), "ini"));
     258       21600 :                                             CATCH_REQUIRE(it != comments.end());
     259       21600 :                                             comments.erase(it);
     260             :                                         }
     261       50400 :                                         if((c & advgetopt::COMMENT_SHELL) != 0)
     262             :                                         {
     263       21600 :                                             auto it(std::find(comments.begin(), comments.end(), "shell"));
     264       21600 :                                             CATCH_REQUIRE(it != comments.end());
     265       21600 :                                             comments.erase(it);
     266             :                                         }
     267       50400 :                                         if((c & advgetopt::COMMENT_CPP) != 0)
     268             :                                         {
     269       21600 :                                             auto it(std::find(comments.begin(), comments.end(), "cpp"));
     270       21600 :                                             CATCH_REQUIRE(it != comments.end());
     271       21600 :                                             comments.erase(it);
     272             :                                         }
     273       50400 :                                         if(c == advgetopt::COMMENT_NONE)
     274             :                                         {
     275        7200 :                                             auto it(std::find(comments.begin(), comments.end(), "none"));
     276        7200 :                                             CATCH_REQUIRE(it != comments.end());
     277        7200 :                                             comments.erase(it);
     278             :                                         }
     279             : 
     280       50400 :                                         CATCH_REQUIRE(comments.empty());
     281       50400 :                                     }
     282       43680 :                                     else if(var_name == "section-operator")
     283             :                                     {
     284       43680 :                                         def_so = false;
     285       43680 :                                         std::vector<std::string> section_operators;
     286       43680 :                                         snapdev::tokenize_string(section_operators, var_value, ",");
     287             : 
     288       43680 :                                         if((so & advgetopt::SECTION_OPERATOR_C) != 0)
     289             :                                         {
     290       23520 :                                             auto it(std::find(section_operators.begin(), section_operators.end(), "c"));
     291       23520 :                                             CATCH_REQUIRE(it != section_operators.end());
     292       23520 :                                             section_operators.erase(it);
     293             :                                         }
     294       43680 :                                         if((so & advgetopt::SECTION_OPERATOR_CPP) != 0)
     295             :                                         {
     296       23520 :                                             auto it(std::find(section_operators.begin(), section_operators.end(), "cpp"));
     297       23520 :                                             CATCH_REQUIRE(it != section_operators.end());
     298       23520 :                                             section_operators.erase(it);
     299             :                                         }
     300       43680 :                                         if((so & advgetopt::SECTION_OPERATOR_BLOCK) != 0)
     301             :                                         {
     302       23520 :                                             auto it(std::find(section_operators.begin(), section_operators.end(), "block"));
     303       23520 :                                             CATCH_REQUIRE(it != section_operators.end());
     304       23520 :                                             section_operators.erase(it);
     305             :                                         }
     306       43680 :                                         if((so & advgetopt::SECTION_OPERATOR_INI_FILE) != 0)
     307             :                                         {
     308       20160 :                                             auto it(std::find(section_operators.begin(), section_operators.end(), "ini-file"));
     309       20160 :                                             CATCH_REQUIRE(it != section_operators.end());
     310       20160 :                                             section_operators.erase(it);
     311             :                                         }
     312             : 
     313       43680 :                                         CATCH_REQUIRE(section_operators.empty());
     314       43680 :                                     }
     315             :                                     else
     316             :                                     {
     317           0 :                                         CATCH_REQUIRE(("unknown var_name = " + var_name) == std::string());
     318             :                                     }
     319      180180 :                                 }
     320             : 
     321       50400 :                                 if(def_lc)
     322             :                                 {
     323        8400 :                                     CATCH_REQUIRE(static_cast<advgetopt::line_continuation_t>(lc) == advgetopt::line_continuation_t::line_continuation_unix);
     324             :                                 }
     325       50400 :                                 if(def_ao)
     326             :                                 {
     327        6300 :                                     CATCH_REQUIRE(real_ao == advgetopt::ASSIGNMENT_OPERATOR_EQUAL);
     328             :                                 }
     329       50400 :                                 if(def_c)
     330             :                                 {
     331           0 :                                     CATCH_REQUIRE(c == (advgetopt::COMMENT_INI | advgetopt::COMMENT_SHELL));
     332             :                                 }
     333       50400 :                             }
     334       50400 :                         }
     335             :                     }
     336             :                 }
     337             :             }
     338           5 :         }
     339             :     }
     340           2 :     CATCH_END_SECTION()
     341             : 
     342           2 :     CATCH_START_SECTION("configuration_setup: check non-existant filename")
     343             :     {
     344           1 :         advgetopt::conf_file_setup setup(
     345             :                       "/etc/advgetopt/unknown-file.conf"
     346             :                     , advgetopt::line_continuation_t::line_continuation_fortran
     347             :                     , advgetopt::ASSIGNMENT_OPERATOR_COLON
     348             :                     , advgetopt::COMMENT_INI
     349           3 :                     , advgetopt::SECTION_OPERATOR_CPP);
     350             : 
     351           1 :         CATCH_REQUIRE(setup.get_original_filename() == "/etc/advgetopt/unknown-file.conf");
     352             : 
     353           1 :         CATCH_REQUIRE(setup.is_valid());
     354           1 :         CATCH_REQUIRE(setup.get_filename() == "/etc/advgetopt/unknown-file.conf");
     355           1 :         CATCH_REQUIRE(setup.get_line_continuation() == advgetopt::line_continuation_t::line_continuation_fortran);
     356           1 :         CATCH_REQUIRE(setup.get_assignment_operator() == advgetopt::ASSIGNMENT_OPERATOR_COLON);
     357           1 :         CATCH_REQUIRE(setup.get_comment() == advgetopt::COMMENT_INI);
     358           1 :         CATCH_REQUIRE(setup.get_section_operator() == advgetopt::SECTION_OPERATOR_CPP);
     359             : 
     360           1 :         CATCH_REQUIRE(setup.get_config_url() == "file:///etc/advgetopt/unknown-file.conf?line-continuation=fortran&assignment-operator=colon&comment=ini&section-operator=cpp");
     361           1 :     }
     362           2 :     CATCH_END_SECTION()
     363           2 : }
     364             : 
     365             : 
     366             : 
     367           1 : CATCH_TEST_CASE("config_reload_tests", "[config][getopt][valid]")
     368             : {
     369           1 :     CATCH_START_SECTION("config_reload_tests: load a file, update it, verify it does not get reloaded")
     370             :     {
     371           1 :         SNAP_CATCH2_NAMESPACE::init_tmp_dir("reload", "load-twice");
     372             : 
     373             :         {
     374           1 :             std::ofstream config_file;
     375           1 :             config_file.open(SNAP_CATCH2_NAMESPACE::g_config_filename, std::ios_base::out | std::ios_base::binary | std::ios_base::trunc);
     376           1 :             CATCH_REQUIRE(config_file.good());
     377             :             config_file <<
     378             :                 "# Auto-generated\n"
     379             :                 "param=value\n"
     380             :                 "changing=without reloading is useless\n"
     381           1 :                 "test=1009\n"
     382             :             ;
     383           1 :         }
     384             : 
     385           1 :         advgetopt::conf_file::pointer_t file1;
     386             :         {
     387           1 :             advgetopt::conf_file_setup setup(SNAP_CATCH2_NAMESPACE::g_config_filename
     388             :                                 , advgetopt::line_continuation_t::line_continuation_single_line
     389             :                                 , advgetopt::ASSIGNMENT_OPERATOR_EQUAL
     390             :                                 , advgetopt::COMMENT_SHELL
     391           1 :                                 , advgetopt::SECTION_OPERATOR_NONE);
     392             : 
     393           1 :             CATCH_REQUIRE(setup.get_original_filename() == SNAP_CATCH2_NAMESPACE::g_config_filename);
     394             : 
     395           1 :             CATCH_REQUIRE(setup.is_valid());
     396           1 :             CATCH_REQUIRE(setup.get_line_continuation() == advgetopt::line_continuation_t::line_continuation_single_line);
     397           1 :             CATCH_REQUIRE(setup.get_assignment_operator() == advgetopt::ASSIGNMENT_OPERATOR_EQUAL);
     398           1 :             CATCH_REQUIRE(setup.get_comment() == advgetopt::COMMENT_SHELL);
     399           1 :             CATCH_REQUIRE(setup.get_section_operator() == advgetopt::SECTION_OPERATOR_NONE);
     400             : 
     401           1 :             file1 = advgetopt::conf_file::get_conf_file(setup);
     402             : 
     403           1 :             CATCH_REQUIRE(file1->get_setup().get_config_url() == setup.get_config_url());
     404           1 :             CATCH_REQUIRE(file1->get_errno() == 0);
     405           1 :             CATCH_REQUIRE(file1->get_sections().empty());
     406           1 :             CATCH_REQUIRE(file1->get_parameters().size() == 3);
     407             : 
     408           1 :             CATCH_REQUIRE(file1->has_parameter("param"));
     409           1 :             CATCH_REQUIRE(file1->has_parameter("changing"));
     410           1 :             CATCH_REQUIRE(file1->has_parameter("test"));
     411             : 
     412           1 :             CATCH_REQUIRE(file1->get_parameter("param") == "value");
     413           1 :             CATCH_REQUIRE(file1->get_parameter("changing") == "without reloading is useless");
     414           1 :             CATCH_REQUIRE(file1->get_parameter("test") == "1009");
     415           1 :         }
     416             : 
     417             :         // change all the values now
     418             :         {
     419           1 :             std::ofstream config_file;
     420           1 :             config_file.open(SNAP_CATCH2_NAMESPACE::g_config_filename, std::ios_base::out | std::ios_base::binary | std::ios_base::trunc);
     421           1 :             CATCH_REQUIRE(config_file.good());
     422             :             config_file <<
     423             :                 "# Auto-generated\n"
     424             :                 "param=new data\n"
     425             :                 "new=this is not even acknowledge\n"
     426             :                 "changing=special value\n"
     427             :                 "test=9010\n"
     428           1 :                 "level=three\n"
     429             :             ;
     430           1 :         }
     431             : 
     432             :         // "reloading" that very same file has the old data
     433             :         {
     434           1 :             advgetopt::conf_file_setup setup(SNAP_CATCH2_NAMESPACE::g_config_filename
     435             :                                 , advgetopt::line_continuation_t::line_continuation_single_line
     436             :                                 , advgetopt::ASSIGNMENT_OPERATOR_EQUAL
     437             :                                 , advgetopt::COMMENT_SHELL
     438           1 :                                 , advgetopt::SECTION_OPERATOR_NONE);
     439             : 
     440           1 :             CATCH_REQUIRE(setup.get_original_filename() == SNAP_CATCH2_NAMESPACE::g_config_filename);
     441             : 
     442           1 :             CATCH_REQUIRE(setup.is_valid());
     443           1 :             CATCH_REQUIRE(setup.get_line_continuation() == advgetopt::line_continuation_t::line_continuation_single_line);
     444           1 :             CATCH_REQUIRE(setup.get_assignment_operator() == advgetopt::ASSIGNMENT_OPERATOR_EQUAL);
     445           1 :             CATCH_REQUIRE(setup.get_comment() == advgetopt::COMMENT_SHELL);
     446           1 :             CATCH_REQUIRE(setup.get_section_operator() == advgetopt::SECTION_OPERATOR_NONE);
     447             : 
     448           1 :             advgetopt::conf_file::pointer_t file(advgetopt::conf_file::get_conf_file(setup));
     449             : 
     450             :             // exact same pointer
     451             :             //
     452           1 :             CATCH_REQUIRE(file == file1);
     453             : 
     454           1 :             CATCH_REQUIRE(file->get_setup().get_config_url() == setup.get_config_url());
     455           1 :             CATCH_REQUIRE(file->get_errno() == 0);
     456           1 :             CATCH_REQUIRE(file->get_sections().empty());
     457           1 :             CATCH_REQUIRE(file->get_parameters().size() == 3);
     458             : 
     459           1 :             CATCH_REQUIRE(file->has_parameter("param"));
     460           1 :             CATCH_REQUIRE(file->has_parameter("changing"));
     461           1 :             CATCH_REQUIRE(file->has_parameter("test"));
     462             : 
     463           1 :             CATCH_REQUIRE(file->get_parameter("param") == "value");
     464           1 :             CATCH_REQUIRE(file->get_parameter("changing") == "without reloading is useless");
     465           1 :             CATCH_REQUIRE(file->get_parameter("test") == "1009");
     466           1 :         }
     467           1 :     }
     468           1 :     CATCH_END_SECTION()
     469           1 : }
     470             : 
     471             : 
     472             : 
     473           1 : CATCH_TEST_CASE("config_duplicated_variables", "[config][getopt][valid]")
     474             : {
     475           1 :     CATCH_START_SECTION("config_duplicated_variables: file with the same variable defined multiple times")
     476             :     {
     477           1 :         SNAP_CATCH2_NAMESPACE::init_tmp_dir("duplicated-variable", "multiple");
     478             : 
     479             :         {
     480           1 :             std::ofstream config_file;
     481           1 :             config_file.open(SNAP_CATCH2_NAMESPACE::g_config_filename, std::ios_base::out | std::ios_base::binary | std::ios_base::trunc);
     482           1 :             CATCH_REQUIRE(config_file.good());
     483             :             config_file <<
     484             :                 "# Auto-generated\n"
     485             :                 "unique    = perfect  \n"
     486             :                 "multiple  = defintions\n"
     487             :                 "another   = just fine \t\n"
     488             :                 "multiple  = value\r\n"
     489             :                 "good      = variable \n"
     490             :                 "multiple  = set\n"
     491           1 :                 "more      = data\t \n"
     492             :             ;
     493           1 :         }
     494             : 
     495           1 :         advgetopt::conf_file_setup setup(SNAP_CATCH2_NAMESPACE::g_config_filename
     496             :                             , advgetopt::line_continuation_t::line_continuation_single_line
     497             :                             , advgetopt::ASSIGNMENT_OPERATOR_EQUAL
     498             :                             , advgetopt::COMMENT_SHELL
     499           1 :                             , advgetopt::SECTION_OPERATOR_NONE);
     500             : 
     501           1 :         CATCH_REQUIRE(setup.get_original_filename() == SNAP_CATCH2_NAMESPACE::g_config_filename);
     502             : 
     503           1 :         CATCH_REQUIRE(setup.is_valid());
     504           1 :         CATCH_REQUIRE(setup.get_line_continuation() == advgetopt::line_continuation_t::line_continuation_single_line);
     505           1 :         CATCH_REQUIRE(setup.get_assignment_operator() == advgetopt::ASSIGNMENT_OPERATOR_EQUAL);
     506           1 :         CATCH_REQUIRE(setup.get_comment() == advgetopt::COMMENT_SHELL);
     507           1 :         CATCH_REQUIRE(setup.get_section_operator() == advgetopt::SECTION_OPERATOR_NONE);
     508             : 
     509           1 :         std::unique_ptr<char, decltype(&::free)> fn(realpath(SNAP_CATCH2_NAMESPACE::g_config_filename.c_str(), nullptr), &::free);
     510           2 :         SNAP_CATCH2_NAMESPACE::push_expected_log(
     511             :                       "warning: parameter \"multiple\" on line 5 in"
     512             :                       " configuration file \""
     513           2 :                     + std::string(fn.get())
     514           3 :                     + "\" was found twice in the same configuration file.");
     515           2 :         SNAP_CATCH2_NAMESPACE::push_expected_log(
     516             :                       "warning: parameter \"multiple\" on line 7 in"
     517             :                       " configuration file \""
     518           2 :                     + std::string(fn.get())
     519           3 :                     + "\" was found twice in the same configuration file.");
     520           1 :         advgetopt::conf_file::pointer_t file(advgetopt::conf_file::get_conf_file(setup));
     521           1 :         SNAP_CATCH2_NAMESPACE::expected_logs_stack_is_empty();
     522             : 
     523           1 :         CATCH_REQUIRE(file->get_setup().get_config_url() == setup.get_config_url());
     524           1 :         CATCH_REQUIRE(file->get_errno() == 0);
     525           1 :         CATCH_REQUIRE(file->get_sections().empty());
     526           1 :         CATCH_REQUIRE(file->get_parameters().size() == 5);
     527             : 
     528           1 :         CATCH_REQUIRE(file->has_parameter("unique"));
     529           1 :         CATCH_REQUIRE(file->has_parameter("another"));
     530           1 :         CATCH_REQUIRE(file->has_parameter("good"));
     531           1 :         CATCH_REQUIRE(file->has_parameter("more"));
     532           1 :         CATCH_REQUIRE(file->has_parameter("multiple"));
     533             : 
     534           1 :         CATCH_REQUIRE(file->get_parameter("unique") == "perfect");
     535           1 :         CATCH_REQUIRE(file->get_parameter("another") == "just fine");
     536           1 :         CATCH_REQUIRE(file->get_parameter("good") == "variable");
     537           1 :         CATCH_REQUIRE(file->get_parameter("more") == "data");
     538           1 :         CATCH_REQUIRE(file->get_parameter("multiple") == "set");
     539             : 
     540             :         // we get a warning while reading; but not when directly
     541             :         // accessing the file object
     542             :         //
     543           1 :         CATCH_REQUIRE(file->set_parameter(std::string(), "multiple", "new value"));
     544           1 :         CATCH_REQUIRE(file->get_parameter("multiple") == "new value");
     545           1 :     }
     546           1 :     CATCH_END_SECTION()
     547           1 : }
     548             : 
     549             : 
     550             : 
     551           1 : CATCH_TEST_CASE("config_callback_calls", "[config][getopt][valid]")
     552             : {
     553           1 :     CATCH_START_SECTION("config_callback_calls: setup a callback and test the set_parameter()/erase() functions")
     554             :     {
     555           1 :         SNAP_CATCH2_NAMESPACE::init_tmp_dir("callback-variable", "callback");
     556             : 
     557             :         {
     558           1 :             std::ofstream config_file;
     559           1 :             config_file.open(SNAP_CATCH2_NAMESPACE::g_config_filename, std::ios_base::out | std::ios_base::binary | std::ios_base::trunc);
     560           1 :             CATCH_REQUIRE(config_file.good());
     561             :             config_file <<
     562             :                 "# Auto-generated\n"
     563             :                 "unique     = perfect  \n"
     564             :                 "definition = long value here\n"
     565             :                 "another    = just fine \t\n"
     566             :                 "multiple   = value\r\n"
     567             :                 "good       = variable \n"
     568             :                 "organized  = set\n"
     569           1 :                 "more       = data\t \n"
     570             :             ;
     571           1 :         }
     572             : 
     573           1 :         advgetopt::conf_file_setup setup(SNAP_CATCH2_NAMESPACE::g_config_filename
     574             :                             , advgetopt::line_continuation_t::line_continuation_single_line
     575             :                             , advgetopt::ASSIGNMENT_OPERATOR_EQUAL
     576             :                             , advgetopt::COMMENT_SHELL
     577           1 :                             , advgetopt::SECTION_OPERATOR_NONE);
     578             : 
     579           1 :         CATCH_REQUIRE(setup.get_original_filename() == SNAP_CATCH2_NAMESPACE::g_config_filename);
     580             : 
     581           1 :         CATCH_REQUIRE(setup.is_valid());
     582           1 :         CATCH_REQUIRE(setup.get_line_continuation() == advgetopt::line_continuation_t::line_continuation_single_line);
     583           1 :         CATCH_REQUIRE(setup.get_assignment_operator() == advgetopt::ASSIGNMENT_OPERATOR_EQUAL);
     584           1 :         CATCH_REQUIRE(setup.get_comment() == advgetopt::COMMENT_SHELL);
     585           1 :         CATCH_REQUIRE(setup.get_section_operator() == advgetopt::SECTION_OPERATOR_NONE);
     586             : 
     587           1 :         advgetopt::conf_file::pointer_t file(advgetopt::conf_file::get_conf_file(setup));
     588             : 
     589             :         struct conf_data
     590             :         {
     591             :             advgetopt::conf_file::pointer_t f_conf_file = advgetopt::conf_file::pointer_t();
     592             :             advgetopt::callback_action_t    f_expected_action = advgetopt::callback_action_t::created;
     593             :             std::string                     f_expected_variable = std::string();
     594             :             std::string                     f_expected_value = std::string();
     595             :         };
     596           1 :         conf_data cf_data;
     597           1 :         cf_data.f_conf_file = file;
     598             : 
     599             :         struct conf_callback
     600             :         {
     601           6 :             void operator () (advgetopt::conf_file::pointer_t conf_file
     602             :                             , advgetopt::callback_action_t action
     603             :                             , std::string const & variable_name
     604             :                             , std::string const & value)
     605             :             {
     606           6 :                 CATCH_REQUIRE(conf_file == f_data->f_conf_file);
     607           6 :                 CATCH_REQUIRE(action == f_data->f_expected_action);
     608           6 :                 CATCH_REQUIRE(variable_name == f_data->f_expected_variable);
     609           6 :                 CATCH_REQUIRE(value == f_data->f_expected_value);
     610           6 :                 CATCH_REQUIRE(conf_file->get_parameter(variable_name) == f_data->f_expected_value);
     611           6 :             }
     612             : 
     613             :             conf_data * f_data = nullptr;
     614             :         };
     615           1 :         conf_callback cf;
     616           1 :         cf.f_data = &cf_data;
     617             : 
     618           1 :         advgetopt::conf_file::callback_id_t callback_id(file->add_callback(cf));
     619             : 
     620           1 :         CATCH_REQUIRE(file->get_setup().get_config_url() == setup.get_config_url());
     621           1 :         CATCH_REQUIRE(file->get_errno() == 0);
     622           1 :         CATCH_REQUIRE_FALSE(file->was_modified());
     623           1 :         CATCH_REQUIRE(file->get_sections().empty());
     624           1 :         CATCH_REQUIRE(file->get_parameters().size() == 7);
     625             : 
     626           1 :         CATCH_REQUIRE(file->has_parameter("unique"));
     627           1 :         CATCH_REQUIRE(file->has_parameter("definition"));
     628           1 :         CATCH_REQUIRE(file->has_parameter("another"));
     629           1 :         CATCH_REQUIRE(file->has_parameter("multiple"));
     630           1 :         CATCH_REQUIRE(file->has_parameter("good"));
     631           1 :         CATCH_REQUIRE(file->has_parameter("organized"));
     632           1 :         CATCH_REQUIRE(file->has_parameter("more"));
     633             : 
     634           1 :         CATCH_REQUIRE(file->get_parameter("unique") == "perfect");
     635           1 :         CATCH_REQUIRE(file->get_parameter("definition") == "long value here");
     636           1 :         CATCH_REQUIRE(file->get_parameter("another") == "just fine");
     637           1 :         CATCH_REQUIRE(file->get_parameter("multiple") == "value");
     638           1 :         CATCH_REQUIRE(file->get_parameter("good") == "variable");
     639           1 :         CATCH_REQUIRE(file->get_parameter("organized") == "set");
     640           1 :         CATCH_REQUIRE(file->get_parameter("more") == "data");
     641             : 
     642             :         // updated action
     643             :         //
     644           1 :         cf_data.f_expected_action = advgetopt::callback_action_t::updated;
     645           1 :         cf_data.f_expected_variable = "multiple";
     646           1 :         cf_data.f_expected_value = "new value";
     647           1 :         CATCH_REQUIRE(file->set_parameter(std::string(), "multiple", "new value"));
     648           1 :         CATCH_REQUIRE(file->was_modified());
     649           1 :         CATCH_REQUIRE(file->get_parameters().size() == 7);
     650           1 :         CATCH_REQUIRE(file->get_parameter("multiple") == "new value");
     651             : 
     652             :         // created action
     653             :         //
     654           1 :         cf_data.f_expected_action = advgetopt::callback_action_t::created;
     655           1 :         cf_data.f_expected_variable = "new-param";
     656           1 :         cf_data.f_expected_value = "with this value";
     657           1 :         CATCH_REQUIRE(file->set_parameter(std::string(), "new_param", "with this value"));
     658           1 :         CATCH_REQUIRE(file->was_modified());
     659           1 :         CATCH_REQUIRE(file->get_parameters().size() == 8);
     660           1 :         CATCH_REQUIRE(file->has_parameter("new-param"));
     661           1 :         CATCH_REQUIRE(file->get_parameter("new-param") == "with this value");
     662           1 :         CATCH_REQUIRE(file->has_parameter("new_param"));
     663           1 :         CATCH_REQUIRE(file->get_parameter("new_param") == "with this value");
     664             : 
     665             :         // updated action when modifying
     666             :         //
     667           1 :         cf_data.f_expected_action = advgetopt::callback_action_t::updated;
     668           1 :         cf_data.f_expected_variable = "new-param";
     669           1 :         cf_data.f_expected_value = "change completely";
     670           1 :         CATCH_REQUIRE(file->set_parameter(std::string(), "new_param", "change completely"));
     671           1 :         CATCH_REQUIRE(file->was_modified());
     672           1 :         CATCH_REQUIRE(file->get_parameters().size() == 8);
     673           1 :         CATCH_REQUIRE(file->has_parameter("new-param"));
     674           1 :         CATCH_REQUIRE(file->get_parameter("new-param") == "change completely");
     675           1 :         CATCH_REQUIRE(file->has_parameter("new_param"));
     676           1 :         CATCH_REQUIRE(file->get_parameter("new_param") == "change completely");
     677             : 
     678             :         // erased action
     679             :         //
     680           1 :         cf_data.f_expected_action = advgetopt::callback_action_t::erased;
     681           1 :         cf_data.f_expected_variable = "new-param";
     682           1 :         cf_data.f_expected_value = std::string();
     683           1 :         CATCH_REQUIRE(file->erase_parameter("new_param"));
     684           1 :         CATCH_REQUIRE(file->was_modified());
     685           1 :         CATCH_REQUIRE(file->get_parameters().size() == 7);
     686           1 :         CATCH_REQUIRE_FALSE(file->has_parameter("new-param"));
     687           1 :         CATCH_REQUIRE(file->get_parameter("new-param") == std::string());
     688           1 :         CATCH_REQUIRE_FALSE(file->has_parameter("new_param"));
     689           1 :         CATCH_REQUIRE(file->get_parameter("new_param") == std::string());
     690           1 :         CATCH_REQUIRE_FALSE(file->erase_parameter("new_param"));
     691             : 
     692             :         // created action again (because it was erased)
     693             :         //
     694           1 :         cf_data.f_expected_action = advgetopt::callback_action_t::created;
     695           1 :         cf_data.f_expected_variable = "new-param";
     696           1 :         cf_data.f_expected_value = "with this value";
     697           1 :         CATCH_REQUIRE(file->set_parameter(std::string(), "new_param", "with this value"));
     698           1 :         CATCH_REQUIRE(file->was_modified());
     699           1 :         CATCH_REQUIRE(file->get_parameters().size() == 8);
     700           1 :         CATCH_REQUIRE(file->has_parameter("new-param"));
     701           1 :         CATCH_REQUIRE(file->get_parameter("new-param") == "with this value");
     702           1 :         CATCH_REQUIRE(file->has_parameter("new_param"));
     703           1 :         CATCH_REQUIRE(file->get_parameter("new_param") == "with this value");
     704             : 
     705           1 :         file->remove_callback(callback_id);
     706           1 :         cf_data.f_expected_action = advgetopt::callback_action_t::created;
     707           1 :         cf_data.f_expected_variable = "ignored";
     708           1 :         cf_data.f_expected_value = "ignored";
     709           1 :         CATCH_REQUIRE(file->set_parameter(std::string(), "new_param", "unnoticed change"));
     710           1 :         CATCH_REQUIRE(file->was_modified());
     711           1 :         CATCH_REQUIRE(file->get_parameters().size() == 8);
     712           1 :         CATCH_REQUIRE(file->has_parameter("new-param"));
     713           1 :         CATCH_REQUIRE(file->get_parameter("new-param") == "unnoticed change");
     714           1 :         CATCH_REQUIRE(file->has_parameter("new_param"));
     715           1 :         CATCH_REQUIRE(file->get_parameter("new_param") == "unnoticed change");
     716             : 
     717             :         // further calls do nothing more
     718             :         //
     719           1 :         file->remove_callback(callback_id);
     720           1 :         CATCH_REQUIRE(file->set_parameter(std::string(), "new_param", "still unnoticed"));
     721           1 :         CATCH_REQUIRE(file->was_modified());
     722           1 :         CATCH_REQUIRE(file->get_parameters().size() == 8);
     723           1 :         CATCH_REQUIRE(file->has_parameter("new-param"));
     724           1 :         CATCH_REQUIRE(file->get_parameter("new-param") == "still unnoticed");
     725           1 :         CATCH_REQUIRE(file->has_parameter("new_param"));
     726           1 :         CATCH_REQUIRE(file->get_parameter("new_param") == "still unnoticed");
     727             : 
     728             :         // and we can always re-add it
     729             :         //
     730           1 :         CATCH_REQUIRE(callback_id != file->add_callback(cf));
     731           1 :         cf_data.f_expected_action = advgetopt::callback_action_t::updated;
     732           1 :         cf_data.f_expected_variable = "new-param";
     733           1 :         cf_data.f_expected_value = "we're back";
     734           1 :         CATCH_REQUIRE(file->set_parameter(std::string(), "new_param", "we're back"));
     735           1 :         CATCH_REQUIRE(file->was_modified());
     736           1 :         CATCH_REQUIRE(file->get_parameters().size() == 8);
     737           1 :         CATCH_REQUIRE(file->has_parameter("new-param"));
     738           1 :         CATCH_REQUIRE(file->get_parameter("new-param") == "we're back");
     739           1 :         CATCH_REQUIRE(file->has_parameter("new_param"));
     740           1 :         CATCH_REQUIRE(file->get_parameter("new_param") == "we're back");
     741             : 
     742             :         // until you save it remains true even if you were to restore the
     743             :         // state to "normal" (we do not keep a copy of the original value
     744             :         // as found in the file.)
     745             :         //
     746           1 :         CATCH_REQUIRE(file->was_modified());
     747           1 :     }
     748           1 :     CATCH_END_SECTION()
     749           1 : }
     750             : 
     751             : 
     752             : 
     753           7 : CATCH_TEST_CASE("config_line_continuation_tests", "[config][getopt][valid]")
     754             : {
     755           7 :     CATCH_START_SECTION("config_line_continuation_tests: single_line (EQUAL)")
     756             :     {
     757           1 :         SNAP_CATCH2_NAMESPACE::init_tmp_dir("line-continuation", "single-line");
     758             : 
     759             :         {
     760           1 :             std::ofstream config_file;
     761           1 :             config_file.open(SNAP_CATCH2_NAMESPACE::g_config_filename, std::ios_base::out | std::ios_base::binary | std::ios_base::trunc);
     762           1 :             CATCH_REQUIRE(config_file.good());
     763             :             config_file <<
     764             :                 "# Auto-generated\n"
     765             :                 "normal=param\n"
     766             :                 "\n"
     767             :                 "rfc-822=start here\n"
     768             :                 "  continue=there\n"
     769             :                 "\n"
     770             :                 "msdos=end with &\n"
     771             :                 "  and-continue=on next line\n"
     772             :                 "\n"
     773             :                 "unix=end with \\\n"
     774             :                 "to-continue=like this\n"
     775             :                 "\n"
     776             :                 "fortran=fortran is funny\n"
     777             :                 "&since=it starts with an & on the following line\n"
     778             :                 "\n"
     779             :                 "semicolon=this ends with\n"
     780           1 :                 "a=semi-colon only;\n"
     781             :             ;
     782           1 :         }
     783             : 
     784           1 :         advgetopt::conf_file_setup setup(SNAP_CATCH2_NAMESPACE::g_config_filename
     785             :                             , advgetopt::line_continuation_t::line_continuation_single_line
     786             :                             , advgetopt::ASSIGNMENT_OPERATOR_EQUAL
     787             :                             , advgetopt::COMMENT_SHELL
     788           1 :                             , advgetopt::SECTION_OPERATOR_NONE);
     789             : 
     790           1 :         CATCH_REQUIRE(setup.get_original_filename() == SNAP_CATCH2_NAMESPACE::g_config_filename);
     791             : 
     792           1 :         CATCH_REQUIRE(setup.is_valid());
     793           1 :         CATCH_REQUIRE(setup.get_line_continuation() == advgetopt::line_continuation_t::line_continuation_single_line);
     794           1 :         CATCH_REQUIRE(setup.get_assignment_operator() == advgetopt::ASSIGNMENT_OPERATOR_EQUAL);
     795           1 :         CATCH_REQUIRE(setup.get_comment() == advgetopt::COMMENT_SHELL);
     796           1 :         CATCH_REQUIRE(setup.get_section_operator() == advgetopt::SECTION_OPERATOR_NONE);
     797             : 
     798           1 :         advgetopt::conf_file::pointer_t file(advgetopt::conf_file::get_conf_file(setup));
     799             : 
     800           1 :         CATCH_REQUIRE(file->get_setup().get_config_url() == setup.get_config_url());
     801           1 :         CATCH_REQUIRE(file->get_errno() == 0);
     802           1 :         CATCH_REQUIRE(file->get_sections().empty());
     803           1 :         CATCH_REQUIRE(file->get_parameters().size() == 11);
     804             : 
     805           1 :         CATCH_REQUIRE(file->has_parameter("normal"));
     806           1 :         CATCH_REQUIRE(file->has_parameter("rfc-822"));
     807           1 :         CATCH_REQUIRE(file->has_parameter("continue"));
     808           1 :         CATCH_REQUIRE(file->has_parameter("msdos"));
     809           1 :         CATCH_REQUIRE(file->has_parameter("and-continue"));
     810           1 :         CATCH_REQUIRE(file->has_parameter("unix"));
     811           1 :         CATCH_REQUIRE(file->has_parameter("to-continue"));
     812           1 :         CATCH_REQUIRE(file->has_parameter("fortran"));
     813           1 :         CATCH_REQUIRE(file->has_parameter("&since"));
     814           1 :         CATCH_REQUIRE(file->has_parameter("semicolon"));
     815           1 :         CATCH_REQUIRE(file->has_parameter("a"));
     816             : 
     817           1 :         CATCH_REQUIRE(file->get_parameter("normal") == "param");
     818           1 :         CATCH_REQUIRE(file->get_parameter("rfc-822") == "start here");
     819           1 :         CATCH_REQUIRE(file->get_parameter("continue") == "there");
     820           1 :         CATCH_REQUIRE(file->get_parameter("msdos") == "end with &");
     821           1 :         CATCH_REQUIRE(file->get_parameter("and-continue") == "on next line");
     822           1 :         CATCH_REQUIRE(file->get_parameter("unix") == "end with \\");
     823           1 :         CATCH_REQUIRE(file->get_parameter("to-continue") == "like this");
     824           1 :         CATCH_REQUIRE(file->get_parameter("fortran") == "fortran is funny");
     825           1 :         CATCH_REQUIRE(file->get_parameter("&since") == "it starts with an & on the following line");
     826           1 :         CATCH_REQUIRE(file->get_parameter("semicolon") == "this ends with");
     827           1 :         CATCH_REQUIRE(file->get_parameter("a") == "semi-colon only;");
     828             : 
     829     1114113 :         for(int c(0); c < 0x110000; ++c)
     830             :         {
     831     1114112 :             if(c >= 0xD800 && c <= 0xDFFFF)
     832             :             {
     833      862208 :                 continue;
     834             :             }
     835      251904 :             std::string const u(libutf8::to_u8string(static_cast<char32_t>(c)));
     836      251904 :             char const * s(u.c_str());
     837      251904 :             if(c == '=')
     838             :             {
     839           1 :                 CATCH_REQUIRE(file->is_assignment_operator(s, false) == advgetopt::assignment_t::ASSIGNMENT_SET);
     840           1 :                 CATCH_REQUIRE(s == u.c_str());
     841           1 :                 CATCH_REQUIRE(file->is_assignment_operator(s, true) == advgetopt::assignment_t::ASSIGNMENT_SET);
     842           1 :                 CATCH_REQUIRE(s == u.c_str() + 1);
     843             :             }
     844             :             else
     845             :             {
     846      251903 :                 CATCH_REQUIRE(file->is_assignment_operator(s, false) == advgetopt::assignment_t::ASSIGNMENT_NONE);
     847      251903 :                 CATCH_REQUIRE(s == u.c_str());
     848      251903 :                 CATCH_REQUIRE(file->is_assignment_operator(s, true) == advgetopt::assignment_t::ASSIGNMENT_NONE);
     849      251903 :                 CATCH_REQUIRE(s == u.c_str());
     850             :             }
     851      251904 :         }
     852           1 :     }
     853           7 :     CATCH_END_SECTION()
     854             : 
     855           7 :     CATCH_START_SECTION("config_line_continuation_tests: single_line (EXTENDED EQUALS)")
     856             :     {
     857           1 :         SNAP_CATCH2_NAMESPACE::init_tmp_dir("extended-equals", "equal-extensions");
     858             : 
     859             :         {
     860           1 :             std::ofstream config_file;
     861           1 :             config_file.open(SNAP_CATCH2_NAMESPACE::g_config_filename, std::ios_base::out | std::ios_base::binary | std::ios_base::trunc);
     862           1 :             CATCH_REQUIRE(config_file.good());
     863             :             config_file <<
     864             :                 "# Auto-generated\n"
     865             :                 "equal=set\n"
     866             :                 "equal=final\n"
     867             :                 "\n"
     868             :                 "optional?=keep-first\n"
     869             :                 "optional?=ignore-second\n"
     870             :                 "\n"
     871             :                 "append=first few words\n"
     872             :                 "append+=\" and a few last words\"\n"
     873             :                 "\n"
     874             :                 "new=only\n"
     875           1 :                 "new:=boom\n"
     876             :             ;
     877           1 :         }
     878             : 
     879           1 :         advgetopt::assignment_operator_t const assignment(
     880             :                   advgetopt::ASSIGNMENT_OPERATOR_EQUAL
     881             :                 | advgetopt::ASSIGNMENT_OPERATOR_EXTENDED);
     882           1 :         advgetopt::conf_file_setup setup(SNAP_CATCH2_NAMESPACE::g_config_filename
     883             :                             , advgetopt::line_continuation_t::line_continuation_single_line
     884             :                             , assignment
     885             :                             , advgetopt::COMMENT_SHELL
     886           1 :                             , advgetopt::SECTION_OPERATOR_NONE);
     887             : 
     888           1 :         CATCH_REQUIRE(setup.get_original_filename() == SNAP_CATCH2_NAMESPACE::g_config_filename);
     889             : 
     890           1 :         CATCH_REQUIRE(setup.is_valid());
     891           1 :         CATCH_REQUIRE(setup.get_line_continuation() == advgetopt::line_continuation_t::line_continuation_single_line);
     892           1 :         CATCH_REQUIRE(setup.get_assignment_operator() == assignment);
     893           1 :         CATCH_REQUIRE(setup.get_comment() == advgetopt::COMMENT_SHELL);
     894           1 :         CATCH_REQUIRE(setup.get_section_operator() == advgetopt::SECTION_OPERATOR_NONE);
     895             : 
     896             :         // the simplest to test the ?=, +=, and := is to capture the errors
     897             :         // here so we do that...
     898             :         //
     899           2 :         SNAP_CATCH2_NAMESPACE::push_expected_log(
     900             :                   "warning: parameter \"equal\" on line 3 in configuration file \""
     901           2 :                 + setup.get_filename()
     902           3 :                 + "\" was found twice in the same configuration file.");
     903           2 :         SNAP_CATCH2_NAMESPACE::push_expected_log(
     904             :                   "warning: parameter \"optional\" on line 6 in configuration file \""
     905           2 :                 + setup.get_filename()
     906           3 :                 + "\" was found twice in the same configuration file.");
     907           2 :         SNAP_CATCH2_NAMESPACE::push_expected_log(
     908             :                   "warning: parameter \"append\" on line 9 in configuration file \""
     909           2 :                 + setup.get_filename()
     910           3 :                 + "\" was found twice in the same configuration file.");
     911           2 :         SNAP_CATCH2_NAMESPACE::push_expected_log(
     912             :                   "warning: parameter \"new\" on line 12 in configuration file \""
     913           2 :                 + setup.get_filename()
     914           3 :                 + "\" was found twice in the same configuration file.");
     915           2 :         SNAP_CATCH2_NAMESPACE::push_expected_log(
     916             :                   "error: parameter \"new\" is already defined and it cannot be overridden"
     917             :                   " with the ':=' operator on line 12 from configuration file \""
     918           2 :                 + setup.get_filename()
     919           3 :                 + "\".");
     920           1 :         advgetopt::conf_file::pointer_t file(advgetopt::conf_file::get_conf_file(setup));
     921           1 :         SNAP_CATCH2_NAMESPACE::expected_logs_stack_is_empty();
     922             : 
     923           1 :         CATCH_REQUIRE(file->get_setup().get_config_url() == setup.get_config_url());
     924           1 :         CATCH_REQUIRE(file->get_errno() == 0);
     925           1 :         CATCH_REQUIRE(file->get_sections().empty());
     926           1 :         CATCH_REQUIRE(file->get_parameters().size() == 4);
     927             : 
     928           1 :         CATCH_REQUIRE(file->has_parameter("equal"));
     929           1 :         CATCH_REQUIRE(file->has_parameter("optional"));
     930           1 :         CATCH_REQUIRE(file->has_parameter("append"));
     931           1 :         CATCH_REQUIRE(file->has_parameter("new"));
     932             : 
     933           1 :         CATCH_REQUIRE(file->get_parameter("equal") == "final");
     934           1 :         CATCH_REQUIRE(file->get_parameter("optional") == "keep-first");
     935           1 :         CATCH_REQUIRE(file->get_parameter("append") == "first few words and a few last words");
     936           1 :         CATCH_REQUIRE(file->get_parameter("new") == "only");
     937             : 
     938           1 :         char const * s = nullptr;
     939     1114113 :         for(int c(0); c < 0x110000; ++c)
     940             :         {
     941     1114112 :             if(c >= 0xD800 && c <= 0xDFFFF)
     942             :             {
     943      862208 :                 continue;
     944             :             }
     945      251904 :             std::string u(libutf8::to_u8string(static_cast<char32_t>(c)));
     946      251904 :             switch(c)
     947             :             {
     948           1 :             case '=':
     949           1 :                 s = u.c_str();
     950           1 :                 CATCH_REQUIRE(file->is_assignment_operator(s, false) == advgetopt::assignment_t::ASSIGNMENT_SET);
     951           1 :                 CATCH_REQUIRE(s == u.c_str());
     952           1 :                 CATCH_REQUIRE(file->is_assignment_operator(s, true) == advgetopt::assignment_t::ASSIGNMENT_SET);
     953           1 :                 CATCH_REQUIRE(s == u.c_str() + 1);
     954           1 :                 break;
     955             : 
     956           1 :             case '+':
     957           1 :                 s = u.c_str();
     958           1 :                 CATCH_REQUIRE(file->is_assignment_operator(s, false) == advgetopt::assignment_t::ASSIGNMENT_NONE);
     959           1 :                 CATCH_REQUIRE(s == u.c_str());
     960           1 :                 CATCH_REQUIRE(file->is_assignment_operator(s, true) == advgetopt::assignment_t::ASSIGNMENT_NONE);
     961           1 :                 CATCH_REQUIRE(s == u.c_str());
     962           1 :                 u += '=';
     963           1 :                 s = u.c_str();
     964           1 :                 CATCH_REQUIRE(file->is_assignment_operator(s, false) == advgetopt::assignment_t::ASSIGNMENT_APPEND);
     965           1 :                 CATCH_REQUIRE(s == u.c_str());
     966           1 :                 CATCH_REQUIRE(file->is_assignment_operator(s, true) == advgetopt::assignment_t::ASSIGNMENT_APPEND);
     967           1 :                 CATCH_REQUIRE(s == u.c_str() + 2);
     968           1 :                 break;
     969             : 
     970           1 :             case '?':
     971           1 :                 s = u.c_str();
     972           1 :                 CATCH_REQUIRE(file->is_assignment_operator(s, false) == advgetopt::assignment_t::ASSIGNMENT_NONE);
     973           1 :                 CATCH_REQUIRE(s == u.c_str());
     974           1 :                 CATCH_REQUIRE(file->is_assignment_operator(s, true) == advgetopt::assignment_t::ASSIGNMENT_NONE);
     975           1 :                 CATCH_REQUIRE(s == u.c_str());
     976           1 :                 u += '=';
     977           1 :                 s = u.c_str();
     978           1 :                 CATCH_REQUIRE(file->is_assignment_operator(s, false) == advgetopt::assignment_t::ASSIGNMENT_OPTIONAL);
     979           1 :                 CATCH_REQUIRE(s == u.c_str());
     980           1 :                 CATCH_REQUIRE(file->is_assignment_operator(s, true) == advgetopt::assignment_t::ASSIGNMENT_OPTIONAL);
     981           1 :                 CATCH_REQUIRE(s == u.c_str() + 2);
     982           1 :                 break;
     983             : 
     984           1 :             case ':':
     985             :                 // note that ':' is a valid assignment, but in this case we
     986             :                 // did not authorize it so it will return NONE
     987             :                 //
     988           1 :                 s = u.c_str();
     989           1 :                 CATCH_REQUIRE(file->is_assignment_operator(s, false) == advgetopt::assignment_t::ASSIGNMENT_NONE);
     990           1 :                 CATCH_REQUIRE(s == u.c_str());
     991           1 :                 CATCH_REQUIRE(file->is_assignment_operator(s, true) == advgetopt::assignment_t::ASSIGNMENT_NONE);
     992           1 :                 CATCH_REQUIRE(s == u.c_str());
     993           1 :                 u += '=';
     994           1 :                 CATCH_REQUIRE(file->is_assignment_operator(s, false) == advgetopt::assignment_t::ASSIGNMENT_NEW);
     995           1 :                 CATCH_REQUIRE(s == u.c_str());
     996           1 :                 CATCH_REQUIRE(file->is_assignment_operator(s, true) == advgetopt::assignment_t::ASSIGNMENT_NEW);
     997           1 :                 CATCH_REQUIRE(s == u.c_str() + 2);
     998           1 :                 break;
     999             : 
    1000      251900 :             default:
    1001      251900 :                 s = u.c_str();
    1002      251900 :                 CATCH_REQUIRE(file->is_assignment_operator(s, false) == advgetopt::assignment_t::ASSIGNMENT_NONE);
    1003      251900 :                 CATCH_REQUIRE(s == u.c_str());
    1004      251900 :                 CATCH_REQUIRE(file->is_assignment_operator(s, true) == advgetopt::assignment_t::ASSIGNMENT_NONE);
    1005      251900 :                 CATCH_REQUIRE(s == u.c_str());
    1006      251900 :                 break;
    1007             : 
    1008             :             }
    1009      251904 :         }
    1010           1 :     }
    1011           7 :     CATCH_END_SECTION()
    1012             : 
    1013           7 :     CATCH_START_SECTION("config_line_continuation_tests: rfc822")
    1014             :     {
    1015           1 :         SNAP_CATCH2_NAMESPACE::init_tmp_dir("line-continuation", "rfc822");
    1016             : 
    1017             :         {
    1018           1 :             std::ofstream config_file;
    1019           1 :             config_file.open(SNAP_CATCH2_NAMESPACE::g_config_filename, std::ios_base::out | std::ios_base::binary | std::ios_base::trunc);
    1020           1 :             CATCH_REQUIRE(config_file.good());
    1021             :             config_file <<
    1022             :                 "# Auto-generated\n"
    1023             :                 "normal=param\n"
    1024             :                 "\n"
    1025             :                 "rfc-822=start here\n"
    1026             :                 "  continue=there\n"
    1027             :                 "\n"
    1028             :                 "msdos=end with &\n"
    1029             :                 "  and-continue=on next line\n"
    1030             :                 "\n"
    1031             :                 "unix=end with \\\n"
    1032             :                 "to-continue=like this\n"
    1033             :                 "\n"
    1034             :                 "fortran=fortran is funny\n"
    1035             :                 "&since=it starts with an & on the following line\n"
    1036             :                 "\n"
    1037             :                 "semicolon=this ends with\n"
    1038           1 :                 "a=semi-colon only;\n"
    1039             :             ;
    1040           1 :         }
    1041             : 
    1042           1 :         advgetopt::conf_file_setup setup(SNAP_CATCH2_NAMESPACE::g_config_filename
    1043             :                             , advgetopt::line_continuation_t::line_continuation_rfc_822
    1044             :                             , advgetopt::ASSIGNMENT_OPERATOR_EQUAL
    1045             :                             , advgetopt::COMMENT_SHELL
    1046           1 :                             , advgetopt::SECTION_OPERATOR_NONE);
    1047             : 
    1048           1 :         CATCH_REQUIRE(setup.get_original_filename() == SNAP_CATCH2_NAMESPACE::g_config_filename);
    1049             : 
    1050           1 :         CATCH_REQUIRE(setup.is_valid());
    1051           1 :         CATCH_REQUIRE(setup.get_line_continuation() == advgetopt::line_continuation_t::line_continuation_rfc_822);
    1052           1 :         CATCH_REQUIRE(setup.get_assignment_operator() == advgetopt::ASSIGNMENT_OPERATOR_EQUAL);
    1053           1 :         CATCH_REQUIRE(setup.get_comment() == advgetopt::COMMENT_SHELL);
    1054           1 :         CATCH_REQUIRE(setup.get_section_operator() == advgetopt::SECTION_OPERATOR_NONE);
    1055             : 
    1056           1 :         advgetopt::conf_file::pointer_t file(advgetopt::conf_file::get_conf_file(setup));
    1057             : 
    1058           1 :         CATCH_REQUIRE(file->get_setup().get_config_url() == setup.get_config_url());
    1059           1 :         CATCH_REQUIRE(file->get_errno() == 0);
    1060           1 :         CATCH_REQUIRE(file->get_sections().empty());
    1061           1 :         CATCH_REQUIRE(file->get_parameters().size() == 9);
    1062             : 
    1063           1 :         CATCH_REQUIRE(file->has_parameter("normal"));
    1064           1 :         CATCH_REQUIRE(file->has_parameter("rfc-822"));
    1065           1 :         CATCH_REQUIRE(file->has_parameter("msdos"));
    1066           1 :         CATCH_REQUIRE_FALSE(file->has_parameter("and-continue"));
    1067           1 :         CATCH_REQUIRE(file->has_parameter("unix"));
    1068           1 :         CATCH_REQUIRE(file->has_parameter("to-continue"));
    1069           1 :         CATCH_REQUIRE(file->has_parameter("fortran"));
    1070           1 :         CATCH_REQUIRE(file->has_parameter("&since"));
    1071           1 :         CATCH_REQUIRE(file->has_parameter("semicolon"));
    1072           1 :         CATCH_REQUIRE(file->has_parameter("a"));
    1073             : 
    1074           1 :         CATCH_REQUIRE(file->get_parameter("normal") == "param");
    1075           1 :         CATCH_REQUIRE(file->get_parameter("rfc-822") == "start herecontinue=there");
    1076           1 :         CATCH_REQUIRE(file->get_parameter("msdos") == "end with &and-continue=on next line");
    1077           1 :         CATCH_REQUIRE(file->get_parameter("and-continue") == std::string());
    1078           1 :         CATCH_REQUIRE(file->get_parameter("unix") == "end with \\");
    1079           1 :         CATCH_REQUIRE(file->get_parameter("to-continue") == "like this");
    1080           1 :         CATCH_REQUIRE(file->get_parameter("fortran") == "fortran is funny");
    1081           1 :         CATCH_REQUIRE(file->get_parameter("&since") == "it starts with an & on the following line");
    1082           1 :         CATCH_REQUIRE(file->get_parameter("semicolon") == "this ends with");
    1083           1 :         CATCH_REQUIRE(file->get_parameter("a") == "semi-colon only;");
    1084           1 :     }
    1085           7 :     CATCH_END_SECTION()
    1086             : 
    1087           7 :     CATCH_START_SECTION("config_line_continuation_tests: msdos")
    1088             :     {
    1089           1 :         SNAP_CATCH2_NAMESPACE::init_tmp_dir("line-continuation", "msdos");
    1090             : 
    1091             :         {
    1092           1 :             std::ofstream config_file;
    1093           1 :             config_file.open(SNAP_CATCH2_NAMESPACE::g_config_filename, std::ios_base::out | std::ios_base::binary | std::ios_base::trunc);
    1094           1 :             CATCH_REQUIRE(config_file.good());
    1095             :             config_file <<
    1096             :                 "# Auto-generated\n"
    1097             :                 "normal=param\n"
    1098             :                 "\n"
    1099             :                 "rfc-822=start here\n"
    1100             :                 "  continue=there\n"
    1101             :                 "\n"
    1102             :                 "msdos=end with &\n"
    1103             :                 "  and-continue=on next line\n"
    1104             :                 "\n"
    1105             :                 "unix=end with \\\n"
    1106             :                 "to-continue=like this\n"
    1107             :                 "\n"
    1108             :                 "fortran=fortran is funny\n"
    1109             :                 "&since=it starts with an & on the following line\n"
    1110             :                 "\n"
    1111             :                 "semicolon=this ends with\n"
    1112           1 :                 "a=semi-colon only;\n"
    1113             :             ;
    1114           1 :         }
    1115             : 
    1116           1 :         advgetopt::conf_file_setup setup(SNAP_CATCH2_NAMESPACE::g_config_filename
    1117             :                             , advgetopt::line_continuation_t::line_continuation_msdos
    1118             :                             , advgetopt::ASSIGNMENT_OPERATOR_EQUAL
    1119             :                             , advgetopt::COMMENT_SHELL
    1120           1 :                             , advgetopt::SECTION_OPERATOR_NONE);
    1121             : 
    1122           1 :         CATCH_REQUIRE(setup.get_original_filename() == SNAP_CATCH2_NAMESPACE::g_config_filename);
    1123             : 
    1124           1 :         CATCH_REQUIRE(setup.is_valid());
    1125           1 :         CATCH_REQUIRE(setup.get_line_continuation() == advgetopt::line_continuation_t::line_continuation_msdos);
    1126           1 :         CATCH_REQUIRE(setup.get_assignment_operator() == advgetopt::ASSIGNMENT_OPERATOR_EQUAL);
    1127           1 :         CATCH_REQUIRE(setup.get_comment() == advgetopt::COMMENT_SHELL);
    1128           1 :         CATCH_REQUIRE(setup.get_section_operator() == advgetopt::SECTION_OPERATOR_NONE);
    1129             : 
    1130           1 :         advgetopt::conf_file::pointer_t file(advgetopt::conf_file::get_conf_file(setup));
    1131             : 
    1132           1 :         CATCH_REQUIRE(file->get_setup().get_config_url() == setup.get_config_url());
    1133           1 :         CATCH_REQUIRE(file->get_errno() == 0);
    1134           1 :         CATCH_REQUIRE(file->get_sections().empty());
    1135           1 :         CATCH_REQUIRE(file->get_parameters().size() == 10);
    1136             : 
    1137           1 :         CATCH_REQUIRE(file->has_parameter("normal"));
    1138           1 :         CATCH_REQUIRE(file->has_parameter("rfc-822"));
    1139           1 :         CATCH_REQUIRE(file->has_parameter("continue"));
    1140           1 :         CATCH_REQUIRE(file->has_parameter("msdos"));
    1141           1 :         CATCH_REQUIRE_FALSE(file->has_parameter("and-continue"));
    1142           1 :         CATCH_REQUIRE(file->has_parameter("unix"));
    1143           1 :         CATCH_REQUIRE(file->has_parameter("to-continue"));
    1144           1 :         CATCH_REQUIRE(file->has_parameter("fortran"));
    1145           1 :         CATCH_REQUIRE(file->has_parameter("&since"));
    1146           1 :         CATCH_REQUIRE(file->has_parameter("semicolon"));
    1147           1 :         CATCH_REQUIRE(file->has_parameter("a"));
    1148             : 
    1149           1 :         CATCH_REQUIRE(file->get_parameter("normal") == "param");
    1150           1 :         CATCH_REQUIRE(file->get_parameter("rfc-822") == "start here");
    1151           1 :         CATCH_REQUIRE(file->get_parameter("continue") == "there");
    1152           1 :         CATCH_REQUIRE(file->get_parameter("msdos") == "end with   and-continue=on next line");
    1153           1 :         CATCH_REQUIRE(file->get_parameter("and-continue") == std::string());
    1154           1 :         CATCH_REQUIRE(file->get_parameter("unix") == "end with \\");
    1155           1 :         CATCH_REQUIRE(file->get_parameter("to-continue") == "like this");
    1156           1 :         CATCH_REQUIRE(file->get_parameter("fortran") == "fortran is funny");
    1157           1 :         CATCH_REQUIRE(file->get_parameter("&since") == "it starts with an & on the following line");
    1158           1 :         CATCH_REQUIRE(file->get_parameter("semicolon") == "this ends with");
    1159           1 :         CATCH_REQUIRE(file->get_parameter("a") == "semi-colon only;");
    1160           1 :     }
    1161           7 :     CATCH_END_SECTION()
    1162             : 
    1163           7 :     CATCH_START_SECTION("config_line_continuation_tests: unix")
    1164             :     {
    1165           1 :         SNAP_CATCH2_NAMESPACE::init_tmp_dir("line-continuation", "unix");
    1166             : 
    1167             :         {
    1168           1 :             std::ofstream config_file;
    1169           1 :             config_file.open(SNAP_CATCH2_NAMESPACE::g_config_filename, std::ios_base::out | std::ios_base::binary | std::ios_base::trunc);
    1170           1 :             CATCH_REQUIRE(config_file.good());
    1171             :             config_file <<
    1172             :                 "# Auto-generated\n"
    1173             :                 "normal=param\n"
    1174             :                 "\n"
    1175             :                 "rfc-822=start here\n"
    1176             :                 "  continue=there\n"
    1177             :                 "\n"
    1178             :                 "msdos=end with &\n"
    1179             :                 "  and-continue=on next line\n"
    1180             :                 "\n"
    1181             :                 "unix=end with \\\n"
    1182             :                 "to-continue=like this\n"
    1183             :                 "\n"
    1184             :                 "fortran=fortran is funny\n"
    1185             :                 "&since=it starts with an & on the following line\n"
    1186             :                 "\n"
    1187             :                 "semicolon=this ends with\n"
    1188           1 :                 "a=semi-colon only;\n"
    1189             :             ;
    1190           1 :         }
    1191             : 
    1192           1 :         advgetopt::conf_file_setup setup(SNAP_CATCH2_NAMESPACE::g_config_filename
    1193             :                             , advgetopt::line_continuation_t::line_continuation_unix
    1194             :                             , advgetopt::ASSIGNMENT_OPERATOR_EQUAL
    1195             :                             , advgetopt::COMMENT_SHELL
    1196           1 :                             , advgetopt::SECTION_OPERATOR_NONE);
    1197             : 
    1198           1 :         CATCH_REQUIRE(setup.get_original_filename() == SNAP_CATCH2_NAMESPACE::g_config_filename);
    1199             : 
    1200           1 :         CATCH_REQUIRE(setup.is_valid());
    1201           1 :         CATCH_REQUIRE(setup.get_line_continuation() == advgetopt::line_continuation_t::line_continuation_unix);
    1202           1 :         CATCH_REQUIRE(setup.get_assignment_operator() == advgetopt::ASSIGNMENT_OPERATOR_EQUAL);
    1203           1 :         CATCH_REQUIRE(setup.get_comment() == advgetopt::COMMENT_SHELL);
    1204           1 :         CATCH_REQUIRE(setup.get_section_operator() == advgetopt::SECTION_OPERATOR_NONE);
    1205             : 
    1206           1 :         advgetopt::conf_file::pointer_t file(advgetopt::conf_file::get_conf_file(setup));
    1207             : 
    1208           1 :         CATCH_REQUIRE(file->get_setup().get_config_url() == setup.get_config_url());
    1209           1 :         CATCH_REQUIRE(file->get_errno() == 0);
    1210           1 :         CATCH_REQUIRE(file->get_sections().empty());
    1211           1 :         CATCH_REQUIRE(file->get_parameters().size() == 10);
    1212             : 
    1213           1 :         CATCH_REQUIRE(file->has_parameter("normal"));
    1214           1 :         CATCH_REQUIRE(file->has_parameter("rfc-822"));
    1215           1 :         CATCH_REQUIRE(file->has_parameter("continue"));
    1216           1 :         CATCH_REQUIRE(file->has_parameter("msdos"));
    1217           1 :         CATCH_REQUIRE(file->has_parameter("and-continue"));
    1218           1 :         CATCH_REQUIRE(file->has_parameter("unix"));
    1219           1 :         CATCH_REQUIRE_FALSE(file->has_parameter("to-continue"));
    1220           1 :         CATCH_REQUIRE(file->has_parameter("fortran"));
    1221           1 :         CATCH_REQUIRE(file->has_parameter("&since"));
    1222           1 :         CATCH_REQUIRE(file->has_parameter("semicolon"));
    1223           1 :         CATCH_REQUIRE(file->has_parameter("a"));
    1224             : 
    1225           1 :         CATCH_REQUIRE(file->get_parameter("normal") == "param");
    1226           1 :         CATCH_REQUIRE(file->get_parameter("rfc-822") == "start here");
    1227           1 :         CATCH_REQUIRE(file->get_parameter("continue") == "there");
    1228           1 :         CATCH_REQUIRE(file->get_parameter("msdos") == "end with &");
    1229           1 :         CATCH_REQUIRE(file->get_parameter("and-continue") == "on next line");
    1230           1 :         CATCH_REQUIRE(file->get_parameter("unix") == "end with to-continue=like this");
    1231           1 :         CATCH_REQUIRE(file->get_parameter("to-continue") == std::string());
    1232           1 :         CATCH_REQUIRE(file->get_parameter("fortran") == "fortran is funny");
    1233           1 :         CATCH_REQUIRE(file->get_parameter("&since") == "it starts with an & on the following line");
    1234           1 :         CATCH_REQUIRE(file->get_parameter("semicolon") == "this ends with");
    1235           1 :         CATCH_REQUIRE(file->get_parameter("a") == "semi-colon only;");
    1236           1 :     }
    1237           7 :     CATCH_END_SECTION()
    1238             : 
    1239           7 :     CATCH_START_SECTION("config_line_continuation_tests: fortran")
    1240             :     {
    1241           1 :         SNAP_CATCH2_NAMESPACE::init_tmp_dir("line-continuation", "fortran");
    1242             : 
    1243             :         {
    1244           1 :             std::ofstream config_file;
    1245           1 :             config_file.open(SNAP_CATCH2_NAMESPACE::g_config_filename, std::ios_base::out | std::ios_base::binary | std::ios_base::trunc);
    1246           1 :             CATCH_REQUIRE(config_file.good());
    1247             :             config_file <<
    1248             :                 "# Auto-generated\n"
    1249             :                 "normal=param\n"
    1250             :                 "\n"
    1251             :                 "rfc-822=start here\n"
    1252             :                 "  continue=there\n"
    1253             :                 "\n"
    1254             :                 "msdos=end with &\n"
    1255             :                 "  and-continue=on next line\n"
    1256             :                 "\n"
    1257             :                 "unix=end with \\\n"
    1258             :                 "to-continue=like this\n"
    1259             :                 "\n"
    1260             :                 "fortran=fortran is funny\n"
    1261             :                 "&since=it starts with an & on the following line\n"
    1262             :                 "\n"
    1263             :                 "semicolon=this ends with\n"
    1264           1 :                 "a=semi-colon only;\n"
    1265             :             ;
    1266           1 :         }
    1267             : 
    1268           1 :         advgetopt::conf_file_setup setup(SNAP_CATCH2_NAMESPACE::g_config_filename
    1269             :                             , advgetopt::line_continuation_t::line_continuation_fortran
    1270             :                             , advgetopt::ASSIGNMENT_OPERATOR_EQUAL
    1271             :                             , advgetopt::COMMENT_SHELL
    1272           1 :                             , advgetopt::SECTION_OPERATOR_NONE);
    1273             : 
    1274           1 :         CATCH_REQUIRE(setup.get_original_filename() == SNAP_CATCH2_NAMESPACE::g_config_filename);
    1275             : 
    1276           1 :         CATCH_REQUIRE(setup.is_valid());
    1277           1 :         CATCH_REQUIRE(setup.get_line_continuation() == advgetopt::line_continuation_t::line_continuation_fortran);
    1278           1 :         CATCH_REQUIRE(setup.get_assignment_operator() == advgetopt::ASSIGNMENT_OPERATOR_EQUAL);
    1279           1 :         CATCH_REQUIRE(setup.get_comment() == advgetopt::COMMENT_SHELL);
    1280           1 :         CATCH_REQUIRE(setup.get_section_operator() == advgetopt::SECTION_OPERATOR_NONE);
    1281             : 
    1282           1 :         advgetopt::conf_file::pointer_t file(advgetopt::conf_file::get_conf_file(setup));
    1283             : 
    1284           1 :         CATCH_REQUIRE(file->get_setup().get_config_url() == setup.get_config_url());
    1285           1 :         CATCH_REQUIRE(file->get_errno() == 0);
    1286           1 :         CATCH_REQUIRE(file->get_sections().empty());
    1287           1 :         CATCH_REQUIRE(file->get_parameters().size() == 10);
    1288             : 
    1289           1 :         CATCH_REQUIRE(file->has_parameter("normal"));
    1290           1 :         CATCH_REQUIRE(file->has_parameter("rfc-822"));
    1291           1 :         CATCH_REQUIRE(file->has_parameter("continue"));
    1292           1 :         CATCH_REQUIRE(file->has_parameter("msdos"));
    1293           1 :         CATCH_REQUIRE(file->has_parameter("and-continue"));
    1294           1 :         CATCH_REQUIRE(file->has_parameter("unix"));
    1295           1 :         CATCH_REQUIRE(file->has_parameter("to-continue"));
    1296           1 :         CATCH_REQUIRE(file->has_parameter("fortran"));
    1297           1 :         CATCH_REQUIRE_FALSE(file->has_parameter("&since"));
    1298           1 :         CATCH_REQUIRE(file->has_parameter("semicolon"));
    1299           1 :         CATCH_REQUIRE(file->has_parameter("a"));
    1300             : 
    1301           1 :         CATCH_REQUIRE(file->get_parameter("normal") == "param");
    1302           1 :         CATCH_REQUIRE(file->get_parameter("rfc-822") == "start here");
    1303           1 :         CATCH_REQUIRE(file->get_parameter("continue") == "there");
    1304           1 :         CATCH_REQUIRE(file->get_parameter("msdos") == "end with &");
    1305           1 :         CATCH_REQUIRE(file->get_parameter("and-continue") == "on next line");
    1306           1 :         CATCH_REQUIRE(file->get_parameter("unix") == "end with \\");
    1307           1 :         CATCH_REQUIRE(file->get_parameter("to-continue") == "like this");
    1308           1 :         CATCH_REQUIRE(file->get_parameter("fortran") == "fortran is funnysince=it starts with an & on the following line");
    1309           1 :         CATCH_REQUIRE(file->get_parameter("&since") == std::string());
    1310           1 :         CATCH_REQUIRE(file->get_parameter("semicolon") == "this ends with");
    1311           1 :         CATCH_REQUIRE(file->get_parameter("a") == "semi-colon only;");
    1312           1 :     }
    1313           7 :     CATCH_END_SECTION()
    1314             : 
    1315           7 :     CATCH_START_SECTION("config_line_continuation_tests: semicolon")
    1316             :     {
    1317           1 :         SNAP_CATCH2_NAMESPACE::init_tmp_dir("line-continuation", "semicolon");
    1318             : 
    1319             :         {
    1320           1 :             std::ofstream config_file;
    1321           1 :             config_file.open(SNAP_CATCH2_NAMESPACE::g_config_filename, std::ios_base::out | std::ios_base::binary | std::ios_base::trunc);
    1322           1 :             CATCH_REQUIRE(config_file.good());
    1323             :             config_file <<
    1324             :                 "# Auto-generated\r\n"
    1325             :                 "normal=param\r\n"
    1326             :                 "\r\n"
    1327             :                 "rfc-822=start here\r\n"
    1328             :                 "  continue=there\r\n"
    1329             :                 "\r\n"
    1330             :                 "msdos=end with &\r"
    1331             :                 "  and-continue=on next line\r\n"
    1332             :                 "\r\n"
    1333             :                 "unix=end with \\\r\n"
    1334             :                 "to-continue=like this\r"
    1335             :                 "\r\n"
    1336             :                 "fortran=fortran is funny\r\n"
    1337             :                 "&since=it starts with an & on the following line\r\n"
    1338             :                 "\r"
    1339             :                 "semicolon=this ends with\r\n"
    1340           1 :                 "a=semi-colon only;\r\n"
    1341             :             ;
    1342           1 :         }
    1343             : 
    1344           1 :         advgetopt::conf_file_setup setup(SNAP_CATCH2_NAMESPACE::g_config_filename
    1345             :                             , advgetopt::line_continuation_t::line_continuation_semicolon
    1346             :                             , advgetopt::ASSIGNMENT_OPERATOR_EQUAL
    1347             :                             , advgetopt::COMMENT_SHELL
    1348           1 :                             , advgetopt::SECTION_OPERATOR_NONE);
    1349             : 
    1350           1 :         CATCH_REQUIRE(setup.get_original_filename() == SNAP_CATCH2_NAMESPACE::g_config_filename);
    1351             : 
    1352           1 :         CATCH_REQUIRE(setup.is_valid());
    1353           1 :         CATCH_REQUIRE(setup.get_line_continuation() == advgetopt::line_continuation_t::line_continuation_semicolon);
    1354           1 :         CATCH_REQUIRE(setup.get_assignment_operator() == advgetopt::ASSIGNMENT_OPERATOR_EQUAL);
    1355           1 :         CATCH_REQUIRE(setup.get_comment() == advgetopt::COMMENT_SHELL);
    1356           1 :         CATCH_REQUIRE(setup.get_section_operator() == advgetopt::SECTION_OPERATOR_NONE);
    1357             : 
    1358           1 :         advgetopt::conf_file::pointer_t file(advgetopt::conf_file::get_conf_file(setup));
    1359             : 
    1360           1 :         CATCH_REQUIRE(file->get_setup().get_config_url() == setup.get_config_url());
    1361           1 :         CATCH_REQUIRE(file->get_errno() == 0);
    1362           1 :         CATCH_REQUIRE(file->get_sections().empty());
    1363           1 :         CATCH_REQUIRE(file->get_parameters().size() == 1);
    1364             : 
    1365           1 :         CATCH_REQUIRE(file->has_parameter("normal"));
    1366           1 :         CATCH_REQUIRE_FALSE(file->has_parameter("rfc-822"));
    1367           1 :         CATCH_REQUIRE_FALSE(file->has_parameter("continue"));
    1368           1 :         CATCH_REQUIRE_FALSE(file->has_parameter("msdos"));
    1369           1 :         CATCH_REQUIRE_FALSE(file->has_parameter("and-continue"));
    1370           1 :         CATCH_REQUIRE_FALSE(file->has_parameter("unix"));
    1371           1 :         CATCH_REQUIRE_FALSE(file->has_parameter("to-continue"));
    1372           1 :         CATCH_REQUIRE_FALSE(file->has_parameter("fortran"));
    1373           1 :         CATCH_REQUIRE_FALSE(file->has_parameter("&since"));
    1374           1 :         CATCH_REQUIRE_FALSE(file->has_parameter("semicolon"));
    1375           1 :         CATCH_REQUIRE_FALSE(file->has_parameter("a"));
    1376             : 
    1377           1 :         CATCH_REQUIRE(file->get_parameter("normal") == std::string("param\n"
    1378             : "\n"
    1379             : "rfc-822=start here\n"
    1380             : "  continue=there\n"
    1381             : "\n"
    1382             : "msdos=end with &\n"
    1383             : "  and-continue=on next line\n"
    1384             : "\n"
    1385             : "unix=end with \\\n"
    1386             : "to-continue=like this\n"
    1387             : "\n"
    1388             : "fortran=fortran is funny\n"
    1389             : "&since=it starts with an & on the following line\n"
    1390             : "\n"
    1391             : "semicolon=this ends with\n"
    1392             : "a=semi-colon only"));
    1393           1 :         CATCH_REQUIRE(file->get_parameter("rfc-822") == std::string());
    1394           1 :         CATCH_REQUIRE(file->get_parameter("continue") == std::string());
    1395           1 :         CATCH_REQUIRE(file->get_parameter("msdos") == std::string());
    1396           1 :         CATCH_REQUIRE(file->get_parameter("and-continue") == std::string());
    1397           1 :         CATCH_REQUIRE(file->get_parameter("unix") == std::string());
    1398           1 :         CATCH_REQUIRE(file->get_parameter("to-continue") == std::string());
    1399           1 :         CATCH_REQUIRE(file->get_parameter("fortran") == std::string());
    1400           1 :         CATCH_REQUIRE(file->get_parameter("&since") == std::string());
    1401           1 :         CATCH_REQUIRE(file->get_parameter("semicolon") == std::string());
    1402           1 :         CATCH_REQUIRE(file->get_parameter("a") == std::string());
    1403           1 :     }
    1404           7 :     CATCH_END_SECTION()
    1405           7 : }
    1406             : 
    1407             : 
    1408             : 
    1409           4 : CATCH_TEST_CASE("config_assignment_operator_tests", "[config][getopt][valid]")
    1410             : {
    1411           4 :     CATCH_START_SECTION("config_assignment_operator_tests: equal")
    1412             :     {
    1413           1 :         SNAP_CATCH2_NAMESPACE::init_tmp_dir("assignment-operator", "equal");
    1414             : 
    1415             :         {
    1416           1 :             std::ofstream config_file;
    1417           1 :             config_file.open(SNAP_CATCH2_NAMESPACE::g_config_filename, std::ios_base::out | std::ios_base::binary | std::ios_base::trunc);
    1418           1 :             CATCH_REQUIRE(config_file.good());
    1419             :             config_file <<
    1420             :                 "# Auto-generated\n"
    1421             :                 "equal=value\n"
    1422             :                 "\n"
    1423             :                 "name_value=127\n"
    1424             :                 "\n"
    1425           1 :                 "and=no operator\n"
    1426             :             ;
    1427           1 :         }
    1428             : 
    1429           1 :         advgetopt::conf_file_setup setup(SNAP_CATCH2_NAMESPACE::g_config_filename
    1430             :                             , advgetopt::line_continuation_t::line_continuation_single_line
    1431             :                             , advgetopt::ASSIGNMENT_OPERATOR_EQUAL
    1432             :                             , advgetopt::COMMENT_SHELL
    1433           1 :                             , advgetopt::SECTION_OPERATOR_NONE);
    1434             : 
    1435           1 :         CATCH_REQUIRE(setup.get_original_filename() == SNAP_CATCH2_NAMESPACE::g_config_filename);
    1436             : 
    1437           1 :         CATCH_REQUIRE(setup.is_valid());
    1438           1 :         CATCH_REQUIRE(setup.get_line_continuation() == advgetopt::line_continuation_t::line_continuation_single_line);
    1439           1 :         CATCH_REQUIRE(setup.get_assignment_operator() == advgetopt::ASSIGNMENT_OPERATOR_EQUAL);
    1440           1 :         CATCH_REQUIRE(setup.get_comment() == advgetopt::COMMENT_SHELL);
    1441           1 :         CATCH_REQUIRE(setup.get_section_operator() == advgetopt::SECTION_OPERATOR_NONE);
    1442             : 
    1443           1 :         advgetopt::conf_file::pointer_t file(advgetopt::conf_file::get_conf_file(setup));
    1444             : 
    1445           1 :         CATCH_REQUIRE(file->get_setup().get_config_url() == setup.get_config_url());
    1446           1 :         CATCH_REQUIRE(file->get_errno() == 0);
    1447           1 :         CATCH_REQUIRE(file->get_sections().empty());
    1448           1 :         CATCH_REQUIRE(file->get_parameters().size() == 3);
    1449             : 
    1450           1 :         CATCH_REQUIRE(file->has_parameter("equal"));
    1451           1 :         CATCH_REQUIRE(file->has_parameter("name-value"));
    1452           1 :         CATCH_REQUIRE(file->has_parameter("and"));
    1453             : 
    1454           1 :         CATCH_REQUIRE(file->get_parameter("equal") == "value");
    1455           1 :         CATCH_REQUIRE(file->get_parameter("name-value") == "127");
    1456           1 :         CATCH_REQUIRE(file->get_parameter("and") == "no operator");
    1457           1 :     }
    1458           4 :     CATCH_END_SECTION()
    1459             : 
    1460           4 :     CATCH_START_SECTION("config_assignment_operator_tests: colon")
    1461             :     {
    1462           1 :         SNAP_CATCH2_NAMESPACE::init_tmp_dir("assignment-operator", "colon");
    1463             : 
    1464             :         {
    1465           1 :             std::ofstream config_file;
    1466           1 :             config_file.open(SNAP_CATCH2_NAMESPACE::g_config_filename, std::ios_base::out | std::ios_base::binary | std::ios_base::trunc);
    1467           1 :             CATCH_REQUIRE(config_file.good());
    1468             :             config_file <<
    1469             :                 "# Auto-generated\n"
    1470             :                 "equal_value\n"
    1471             :                 "\n"
    1472             :                 "name:value=127\n"
    1473             :                 "\n"
    1474           1 :                 "and_no-operator\n"
    1475             :             ;
    1476           1 :         }
    1477             : 
    1478           1 :         advgetopt::conf_file_setup setup(SNAP_CATCH2_NAMESPACE::g_config_filename
    1479             :                             , advgetopt::line_continuation_t::line_continuation_single_line
    1480             :                             , advgetopt::ASSIGNMENT_OPERATOR_COLON
    1481             :                             , advgetopt::COMMENT_SHELL
    1482           1 :                             , advgetopt::SECTION_OPERATOR_NONE);
    1483             : 
    1484           1 :         CATCH_REQUIRE(setup.get_original_filename() == SNAP_CATCH2_NAMESPACE::g_config_filename);
    1485             : 
    1486           1 :         CATCH_REQUIRE(setup.is_valid());
    1487           1 :         CATCH_REQUIRE(setup.get_line_continuation() == advgetopt::line_continuation_t::line_continuation_single_line);
    1488           1 :         CATCH_REQUIRE(setup.get_assignment_operator() == advgetopt::ASSIGNMENT_OPERATOR_COLON);
    1489           1 :         CATCH_REQUIRE(setup.get_comment() == advgetopt::COMMENT_SHELL);
    1490           1 :         CATCH_REQUIRE(setup.get_section_operator() == advgetopt::SECTION_OPERATOR_NONE);
    1491             : 
    1492           1 :         advgetopt::conf_file::pointer_t file(advgetopt::conf_file::get_conf_file(setup));
    1493             : 
    1494           1 :         CATCH_REQUIRE(file->get_setup().get_config_url() == setup.get_config_url());
    1495           1 :         CATCH_REQUIRE(file->get_errno() == 0);
    1496           1 :         CATCH_REQUIRE(file->get_sections().empty());
    1497           1 :         CATCH_REQUIRE(file->get_parameters().size() == 3);
    1498             : 
    1499           1 :         CATCH_REQUIRE(file->has_parameter("equal-value"));
    1500           1 :         CATCH_REQUIRE(file->has_parameter("name"));
    1501           1 :         CATCH_REQUIRE(file->has_parameter("and-no-operator"));
    1502             : 
    1503           1 :         CATCH_REQUIRE(file->get_parameter("equal-value") == std::string());
    1504           1 :         CATCH_REQUIRE(file->get_parameter("name") == "value=127");
    1505           1 :         CATCH_REQUIRE(file->get_parameter("and-no-operator") == std::string());
    1506           1 :     }
    1507           4 :     CATCH_END_SECTION()
    1508             : 
    1509           4 :     CATCH_START_SECTION("config_assignment_operator_tests: space")
    1510             :     {
    1511           1 :         SNAP_CATCH2_NAMESPACE::init_tmp_dir("assignment-operator", "space");
    1512             : 
    1513             :         {
    1514           1 :             std::ofstream config_file;
    1515           1 :             config_file.open(SNAP_CATCH2_NAMESPACE::g_config_filename, std::ios_base::out | std::ios_base::binary | std::ios_base::trunc);
    1516           1 :             CATCH_REQUIRE(config_file.good());
    1517             :             config_file <<
    1518             :                 "# Auto-generated\n"
    1519             :                 "equal-value\n"
    1520             :                 "\n"
    1521             :                 "name 127\n"
    1522             :                 "\n"
    1523           1 :                 "and-no operator\n"
    1524             :             ;
    1525           1 :         }
    1526             : 
    1527           1 :         advgetopt::conf_file_setup setup(SNAP_CATCH2_NAMESPACE::g_config_filename
    1528             :                             , advgetopt::line_continuation_t::line_continuation_single_line
    1529             :                             , advgetopt::ASSIGNMENT_OPERATOR_SPACE
    1530             :                             , advgetopt::COMMENT_SHELL
    1531           1 :                             , advgetopt::SECTION_OPERATOR_NONE);
    1532             : 
    1533           1 :         CATCH_REQUIRE(setup.get_original_filename() == SNAP_CATCH2_NAMESPACE::g_config_filename);
    1534             : 
    1535           1 :         CATCH_REQUIRE(setup.is_valid());
    1536           1 :         CATCH_REQUIRE(setup.get_line_continuation() == advgetopt::line_continuation_t::line_continuation_single_line);
    1537           1 :         CATCH_REQUIRE(setup.get_assignment_operator() == advgetopt::ASSIGNMENT_OPERATOR_SPACE);
    1538           1 :         CATCH_REQUIRE(setup.get_comment() == advgetopt::COMMENT_SHELL);
    1539           1 :         CATCH_REQUIRE(setup.get_section_operator() == advgetopt::SECTION_OPERATOR_NONE);
    1540             : 
    1541           1 :         advgetopt::conf_file::pointer_t file(advgetopt::conf_file::get_conf_file(setup));
    1542             : 
    1543           1 :         CATCH_REQUIRE(file->get_setup().get_config_url() == setup.get_config_url());
    1544           1 :         CATCH_REQUIRE(file->get_errno() == 0);
    1545           1 :         CATCH_REQUIRE(file->get_sections().empty());
    1546           1 :         CATCH_REQUIRE(file->get_parameters().size() == 3);
    1547             : 
    1548           1 :         CATCH_REQUIRE(file->has_parameter("equal-value"));
    1549           1 :         CATCH_REQUIRE(file->has_parameter("name"));
    1550           1 :         CATCH_REQUIRE(file->has_parameter("and-no"));
    1551             : 
    1552           1 :         CATCH_REQUIRE(file->get_parameter("equal-value") == std::string());
    1553           1 :         CATCH_REQUIRE(file->get_parameter("name") == "127");
    1554           1 :         CATCH_REQUIRE(file->get_parameter("and-no") == "operator");
    1555           1 :     }
    1556           4 :     CATCH_END_SECTION()
    1557             : 
    1558           4 :     CATCH_START_SECTION("config_assignment_operator_tests: equal_colon_and_space")
    1559             :     {
    1560           1 :         SNAP_CATCH2_NAMESPACE::init_tmp_dir("assignment-operator", "all");
    1561             : 
    1562             :         {
    1563           1 :             std::ofstream config_file;
    1564           1 :             config_file.open(SNAP_CATCH2_NAMESPACE::g_config_filename, std::ios_base::out | std::ios_base::binary | std::ios_base::trunc);
    1565           1 :             CATCH_REQUIRE(config_file.good());
    1566             :             config_file <<
    1567             :                 "# Auto-generated\n"
    1568             :                 "equal=value\n"
    1569             :                 "\n"
    1570             :                 "name: 127\n"
    1571             :                 "\n"
    1572           1 :                 "and no operator\n"
    1573             :             ;
    1574           1 :         }
    1575             : 
    1576           1 :         advgetopt::conf_file_setup setup(SNAP_CATCH2_NAMESPACE::g_config_filename
    1577             :                             , advgetopt::line_continuation_t::line_continuation_single_line
    1578             :                             , advgetopt::ASSIGNMENT_OPERATOR_EQUAL
    1579             :                             | advgetopt::ASSIGNMENT_OPERATOR_COLON
    1580             :                             | advgetopt::ASSIGNMENT_OPERATOR_SPACE
    1581             :                             , advgetopt::COMMENT_SHELL
    1582           1 :                             , advgetopt::SECTION_OPERATOR_NONE);
    1583             : 
    1584           1 :         CATCH_REQUIRE(setup.get_original_filename() == SNAP_CATCH2_NAMESPACE::g_config_filename);
    1585             : 
    1586           1 :         CATCH_REQUIRE(setup.is_valid());
    1587           1 :         CATCH_REQUIRE(setup.get_line_continuation() == advgetopt::line_continuation_t::line_continuation_single_line);
    1588           1 :         CATCH_REQUIRE(setup.get_assignment_operator() == (advgetopt::ASSIGNMENT_OPERATOR_EQUAL
    1589             :                                                        | advgetopt::ASSIGNMENT_OPERATOR_COLON
    1590             :                                                        | advgetopt::ASSIGNMENT_OPERATOR_SPACE));
    1591           1 :         CATCH_REQUIRE(setup.get_comment() == advgetopt::COMMENT_SHELL);
    1592           1 :         CATCH_REQUIRE(setup.get_section_operator() == advgetopt::SECTION_OPERATOR_NONE);
    1593             : 
    1594           1 :         advgetopt::conf_file::pointer_t file(advgetopt::conf_file::get_conf_file(setup));
    1595             : 
    1596           1 :         CATCH_REQUIRE(file->get_setup().get_config_url() == setup.get_config_url());
    1597           1 :         CATCH_REQUIRE(file->get_errno() == 0);
    1598           1 :         CATCH_REQUIRE(file->get_sections().empty());
    1599           1 :         CATCH_REQUIRE(file->get_parameters().size() == 3);
    1600             : 
    1601           1 :         CATCH_REQUIRE(file->has_parameter("equal"));
    1602           1 :         CATCH_REQUIRE(file->has_parameter("name"));
    1603           1 :         CATCH_REQUIRE(file->has_parameter("and"));
    1604             : 
    1605           1 :         CATCH_REQUIRE(file->get_parameter("equal") == "value");
    1606           1 :         CATCH_REQUIRE(file->get_parameter("name") == "127");
    1607           1 :         CATCH_REQUIRE(file->get_parameter("and") == "no operator");
    1608           1 :     }
    1609           4 :     CATCH_END_SECTION()
    1610           4 : }
    1611             : 
    1612             : 
    1613             : 
    1614             : 
    1615             : 
    1616           4 : CATCH_TEST_CASE("config_comment_tests", "[config][getopt][valid]")
    1617             : {
    1618           4 :     CATCH_START_SECTION("config_comment_tests: ini comment")
    1619             :     {
    1620           1 :         SNAP_CATCH2_NAMESPACE::init_tmp_dir("comment", "ini");
    1621             : 
    1622             :         {
    1623           1 :             std::ofstream config_file;
    1624           1 :             config_file.open(SNAP_CATCH2_NAMESPACE::g_config_filename, std::ios_base::out | std::ios_base::binary | std::ios_base::trunc);
    1625           1 :             CATCH_REQUIRE(config_file.good());
    1626             :             config_file <<
    1627             :                 "; Auto-generated\n"
    1628             :                 "ini=comment\n"
    1629             :                 ";ignore=this one\n"
    1630             :                 "is=the semi-colon\n"
    1631             :                 ";continuation=with Unix\\\n"
    1632             :                 "also=works for\\\n"
    1633           1 :                 "comments\n"
    1634             :             ;
    1635           1 :         }
    1636             : 
    1637           1 :         advgetopt::conf_file_setup setup(SNAP_CATCH2_NAMESPACE::g_config_filename
    1638             :                             , advgetopt::line_continuation_t::line_continuation_unix
    1639             :                             , advgetopt::ASSIGNMENT_OPERATOR_EQUAL
    1640             :                             , advgetopt::COMMENT_INI
    1641           1 :                             , advgetopt::SECTION_OPERATOR_NONE);
    1642             : 
    1643           1 :         CATCH_REQUIRE(setup.get_original_filename() == SNAP_CATCH2_NAMESPACE::g_config_filename);
    1644             : 
    1645           1 :         CATCH_REQUIRE(setup.is_valid());
    1646           1 :         CATCH_REQUIRE(setup.get_line_continuation() == advgetopt::line_continuation_t::line_continuation_unix);
    1647           1 :         CATCH_REQUIRE(setup.get_assignment_operator() == advgetopt::ASSIGNMENT_OPERATOR_EQUAL);
    1648           1 :         CATCH_REQUIRE(setup.get_comment() == advgetopt::COMMENT_INI);
    1649           1 :         CATCH_REQUIRE(setup.get_section_operator() == advgetopt::SECTION_OPERATOR_NONE);
    1650             : 
    1651           1 :         advgetopt::conf_file::pointer_t file(advgetopt::conf_file::get_conf_file(setup));
    1652             : 
    1653           1 :         CATCH_REQUIRE(file->get_setup().get_config_url() == setup.get_config_url());
    1654           1 :         CATCH_REQUIRE(file->get_errno() == 0);
    1655           1 :         CATCH_REQUIRE(file->get_sections().empty());
    1656           1 :         CATCH_REQUIRE(file->get_parameters().size() == 2);
    1657             : 
    1658           1 :         CATCH_REQUIRE(file->has_parameter("ini"));
    1659           1 :         CATCH_REQUIRE(file->has_parameter("is"));
    1660             : 
    1661           1 :         CATCH_REQUIRE(file->get_parameter("ini") == "comment");
    1662           1 :         CATCH_REQUIRE(file->get_parameter("is") == "the semi-colon");
    1663           1 :     }
    1664           4 :     CATCH_END_SECTION()
    1665             : 
    1666           4 :     CATCH_START_SECTION("config_comment_tests: shell comment")
    1667             :     {
    1668           1 :         SNAP_CATCH2_NAMESPACE::init_tmp_dir("comment", "shell");
    1669             : 
    1670             :         {
    1671           1 :             std::ofstream config_file;
    1672           1 :             config_file.open(SNAP_CATCH2_NAMESPACE::g_config_filename, std::ios_base::out | std::ios_base::binary | std::ios_base::trunc);
    1673           1 :             CATCH_REQUIRE(config_file.good());
    1674             :             config_file <<
    1675             :                 "# Auto-generated\n"
    1676             :                 "shell=comment\n"
    1677             :                 "#ignore=this one\n"
    1678             :                 "is=the hash (`#`) character\n"
    1679             :                 "#continuation=with Unix\\\n"
    1680             :                 "also=works for\\\n"
    1681           1 :                 "comments\n"
    1682             :             ;
    1683           1 :         }
    1684             : 
    1685           1 :         advgetopt::conf_file_setup setup(SNAP_CATCH2_NAMESPACE::g_config_filename
    1686             :                             , advgetopt::line_continuation_t::line_continuation_unix
    1687             :                             , advgetopt::ASSIGNMENT_OPERATOR_EQUAL
    1688             :                             , advgetopt::COMMENT_SHELL
    1689           1 :                             , advgetopt::SECTION_OPERATOR_NONE);
    1690             : 
    1691           1 :         CATCH_REQUIRE(setup.get_original_filename() == SNAP_CATCH2_NAMESPACE::g_config_filename);
    1692             : 
    1693           1 :         CATCH_REQUIRE(setup.is_valid());
    1694           1 :         CATCH_REQUIRE(setup.get_line_continuation() == advgetopt::line_continuation_t::line_continuation_unix);
    1695           1 :         CATCH_REQUIRE(setup.get_assignment_operator() == advgetopt::ASSIGNMENT_OPERATOR_EQUAL);
    1696           1 :         CATCH_REQUIRE(setup.get_comment() == advgetopt::COMMENT_SHELL);
    1697           1 :         CATCH_REQUIRE(setup.get_section_operator() == advgetopt::SECTION_OPERATOR_NONE);
    1698             : 
    1699           1 :         advgetopt::conf_file::pointer_t file(advgetopt::conf_file::get_conf_file(setup));
    1700             : 
    1701           1 :         CATCH_REQUIRE(file->get_setup().get_config_url() == setup.get_config_url());
    1702           1 :         CATCH_REQUIRE(file->get_errno() == 0);
    1703           1 :         CATCH_REQUIRE(file->get_sections().empty());
    1704           1 :         CATCH_REQUIRE(file->get_parameters().size() == 2);
    1705             : 
    1706           1 :         CATCH_REQUIRE(file->has_parameter("shell"));
    1707           1 :         CATCH_REQUIRE(file->has_parameter("is"));
    1708             : 
    1709           1 :         CATCH_REQUIRE(file->get_parameter("shell") == "comment");
    1710           1 :         CATCH_REQUIRE(file->get_parameter("is") == "the hash (`#`) character");
    1711           1 :     }
    1712           4 :     CATCH_END_SECTION()
    1713             : 
    1714           4 :     CATCH_START_SECTION("config_comment_tests: C++ comment")
    1715             :     {
    1716           1 :         SNAP_CATCH2_NAMESPACE::init_tmp_dir("comment", "cpp");
    1717             : 
    1718             :         {
    1719           1 :             std::ofstream config_file;
    1720           1 :             config_file.open(SNAP_CATCH2_NAMESPACE::g_config_filename, std::ios_base::out | std::ios_base::binary | std::ios_base::trunc);
    1721           1 :             CATCH_REQUIRE(config_file.good());
    1722             :             config_file <<
    1723             :                 "// Auto-generated\n"
    1724             :                 "cpp=comment\n"
    1725             :                 "//ignore=this one\n"
    1726             :                 "is=the double slash (`//`)\n"
    1727             :                 "//continuation=with Unix\\\n"
    1728             :                 "also=works for\\\n"
    1729           1 :                 "comments\n"
    1730             :             ;
    1731           1 :         }
    1732             : 
    1733           1 :         advgetopt::conf_file_setup setup(SNAP_CATCH2_NAMESPACE::g_config_filename
    1734             :                             , advgetopt::line_continuation_t::line_continuation_unix
    1735             :                             , advgetopt::ASSIGNMENT_OPERATOR_EQUAL
    1736             :                             , advgetopt::COMMENT_CPP
    1737           1 :                             , advgetopt::SECTION_OPERATOR_NONE);
    1738             : 
    1739           1 :         CATCH_REQUIRE(setup.get_original_filename() == SNAP_CATCH2_NAMESPACE::g_config_filename);
    1740             : 
    1741           1 :         CATCH_REQUIRE(setup.is_valid());
    1742           1 :         CATCH_REQUIRE(setup.get_line_continuation() == advgetopt::line_continuation_t::line_continuation_unix);
    1743           1 :         CATCH_REQUIRE(setup.get_assignment_operator() == advgetopt::ASSIGNMENT_OPERATOR_EQUAL);
    1744           1 :         CATCH_REQUIRE(setup.get_comment() == advgetopt::COMMENT_CPP);
    1745           1 :         CATCH_REQUIRE(setup.get_section_operator() == advgetopt::SECTION_OPERATOR_NONE);
    1746             : 
    1747           1 :         advgetopt::conf_file::pointer_t file(advgetopt::conf_file::get_conf_file(setup));
    1748             : 
    1749           1 :         CATCH_REQUIRE(file->get_setup().get_config_url() == setup.get_config_url());
    1750           1 :         CATCH_REQUIRE(file->get_errno() == 0);
    1751           1 :         CATCH_REQUIRE(file->get_sections().empty());
    1752           1 :         CATCH_REQUIRE(file->get_parameters().size() == 2);
    1753             : 
    1754           1 :         CATCH_REQUIRE(file->has_parameter("cpp"));
    1755           1 :         CATCH_REQUIRE(file->has_parameter("is"));
    1756             : 
    1757           1 :         CATCH_REQUIRE(file->get_parameter("cpp") == "comment");
    1758           1 :         CATCH_REQUIRE(file->get_parameter("is") == "the double slash (`//`)");
    1759           1 :     }
    1760           4 :     CATCH_END_SECTION()
    1761             : 
    1762           4 :     CATCH_START_SECTION("config_comment_tests: all three comments")
    1763             :     {
    1764           1 :         SNAP_CATCH2_NAMESPACE::init_tmp_dir("comment", "all-comments");
    1765             : 
    1766             :         {
    1767           1 :             std::ofstream config_file;
    1768           1 :             config_file.open(SNAP_CATCH2_NAMESPACE::g_config_filename, std::ios_base::out | std::ios_base::binary | std::ios_base::trunc);
    1769           1 :             CATCH_REQUIRE(config_file.good());
    1770             :             config_file <<
    1771             :                 "// Auto-generated\n"
    1772             :                 "all=comments\n"
    1773             :                 ";ignore=this one\n"
    1774             :                 "together=for powerful config support\n"
    1775             :                 "#continuation=with Unix\\\n"
    1776             :                 "also=works for\\\n"
    1777             :                 "comments\n"
    1778             :                 "but=maybe\n"
    1779             :                 ";we=should\\\n"
    1780             :                 "test=continuation\n"
    1781             :                 "//with=each\\\n"
    1782           1 :                 "each=type of comment\n"
    1783             :             ;
    1784           1 :         }
    1785             : 
    1786           1 :         advgetopt::conf_file_setup setup(SNAP_CATCH2_NAMESPACE::g_config_filename
    1787             :                             , advgetopt::line_continuation_t::line_continuation_unix
    1788             :                             , advgetopt::ASSIGNMENT_OPERATOR_EQUAL
    1789             :                             , advgetopt::COMMENT_INI | advgetopt::COMMENT_SHELL | advgetopt::COMMENT_CPP
    1790           1 :                             , advgetopt::SECTION_OPERATOR_NONE);
    1791             : 
    1792           1 :         CATCH_REQUIRE(setup.get_original_filename() == SNAP_CATCH2_NAMESPACE::g_config_filename);
    1793             : 
    1794           1 :         CATCH_REQUIRE(setup.is_valid());
    1795           1 :         CATCH_REQUIRE(setup.get_line_continuation() == advgetopt::line_continuation_t::line_continuation_unix);
    1796           1 :         CATCH_REQUIRE(setup.get_assignment_operator() == advgetopt::ASSIGNMENT_OPERATOR_EQUAL);
    1797           1 :         CATCH_REQUIRE(setup.get_comment() == (advgetopt::COMMENT_INI | advgetopt::COMMENT_SHELL | advgetopt::COMMENT_CPP));
    1798           1 :         CATCH_REQUIRE(setup.get_section_operator() == advgetopt::SECTION_OPERATOR_NONE);
    1799             : 
    1800           1 :         advgetopt::conf_file::pointer_t file(advgetopt::conf_file::get_conf_file(setup));
    1801             : 
    1802           1 :         CATCH_REQUIRE(file->get_setup().get_config_url() == setup.get_config_url());
    1803           1 :         CATCH_REQUIRE(file->get_errno() == 0);
    1804           1 :         CATCH_REQUIRE(file->get_sections().empty());
    1805           1 :         CATCH_REQUIRE(file->get_parameters().size() == 3);
    1806             : 
    1807           1 :         CATCH_REQUIRE(file->has_parameter("all"));
    1808           1 :         CATCH_REQUIRE(file->has_parameter("together"));
    1809           1 :         CATCH_REQUIRE(file->has_parameter("but"));
    1810             : 
    1811           1 :         CATCH_REQUIRE(file->get_parameter("all") == "comments");
    1812           1 :         CATCH_REQUIRE(file->get_parameter("together") == "for powerful config support");
    1813           1 :         CATCH_REQUIRE(file->get_parameter("but") == "maybe");
    1814           1 :     }
    1815           4 :     CATCH_END_SECTION()
    1816           4 : }
    1817             : 
    1818             : 
    1819             : 
    1820             : 
    1821             : 
    1822           7 : CATCH_TEST_CASE("config_section_tests", "[config][getopt][valid]")
    1823             : {
    1824           7 :     CATCH_START_SECTION("config_section_tests: section operator c (.)")
    1825             :     {
    1826           1 :         SNAP_CATCH2_NAMESPACE::init_tmp_dir("section-operator", "section-c");
    1827             : 
    1828             :         {
    1829           1 :             std::ofstream config_file;
    1830           1 :             config_file.open(SNAP_CATCH2_NAMESPACE::g_config_filename, std::ios_base::out | std::ios_base::binary | std::ios_base::trunc);
    1831           1 :             CATCH_REQUIRE(config_file.good());
    1832             :             config_file <<
    1833             :                 "# Auto-generated\n"
    1834             :                 "a=color\n"
    1835             :                 "a.b=red\n"
    1836             :                 "a.b.c=122\n"
    1837             :                 "m=size\n"
    1838             :                 "z=edge\n"
    1839             :                 "z.b=line\n"
    1840           1 :                 "z.b.c=12.72\n"
    1841             :             ;
    1842           1 :         }
    1843             : 
    1844           1 :         advgetopt::conf_file_setup setup(SNAP_CATCH2_NAMESPACE::g_config_filename
    1845             :                             , advgetopt::line_continuation_t::line_continuation_unix
    1846             :                             , advgetopt::ASSIGNMENT_OPERATOR_EQUAL
    1847             :                             , advgetopt::COMMENT_SHELL
    1848           1 :                             , advgetopt::SECTION_OPERATOR_C);
    1849             : 
    1850           1 :         CATCH_REQUIRE(setup.get_original_filename() == SNAP_CATCH2_NAMESPACE::g_config_filename);
    1851             : 
    1852           1 :         CATCH_REQUIRE(setup.is_valid());
    1853           1 :         CATCH_REQUIRE(setup.get_line_continuation() == advgetopt::line_continuation_t::line_continuation_unix);
    1854           1 :         CATCH_REQUIRE(setup.get_assignment_operator() == advgetopt::ASSIGNMENT_OPERATOR_EQUAL);
    1855           1 :         CATCH_REQUIRE(setup.get_comment() == advgetopt::COMMENT_SHELL);
    1856           1 :         CATCH_REQUIRE(setup.get_section_operator() == advgetopt::SECTION_OPERATOR_C);
    1857             : 
    1858           1 :         advgetopt::conf_file::pointer_t file(advgetopt::conf_file::get_conf_file(setup));
    1859             : 
    1860           1 :         CATCH_REQUIRE(file->get_setup().get_config_url() == setup.get_config_url());
    1861           1 :         CATCH_REQUIRE(file->get_errno() == 0);
    1862             : 
    1863           1 :         advgetopt::conf_file::sections_t sections(file->get_sections());
    1864           1 :         CATCH_REQUIRE(sections.size() == 4);
    1865           1 :         CATCH_REQUIRE(sections.find("a")    != sections.end());
    1866           1 :         CATCH_REQUIRE(sections.find("a::b") != sections.end());
    1867           1 :         CATCH_REQUIRE(sections.find("z")    != sections.end());
    1868           1 :         CATCH_REQUIRE(sections.find("z::b") != sections.end());
    1869             : 
    1870           1 :         CATCH_REQUIRE(file->get_parameters().size() == 7);
    1871             : 
    1872           1 :         CATCH_REQUIRE(file->has_parameter("a"));
    1873           1 :         CATCH_REQUIRE(file->has_parameter("a::b"));
    1874           1 :         CATCH_REQUIRE(file->has_parameter("a::b::c"));
    1875           1 :         CATCH_REQUIRE(file->has_parameter("m"));
    1876           1 :         CATCH_REQUIRE(file->has_parameter("z"));
    1877           1 :         CATCH_REQUIRE(file->has_parameter("z::b"));
    1878           1 :         CATCH_REQUIRE(file->has_parameter("z::b::c"));
    1879             : 
    1880           1 :         CATCH_REQUIRE(file->get_parameter("a") == "color");
    1881           1 :         CATCH_REQUIRE(file->get_parameter("a::b") == "red");
    1882           1 :         CATCH_REQUIRE(file->get_parameter("a::b::c") == "122");
    1883           1 :         CATCH_REQUIRE(file->get_parameter("m") == "size");
    1884           1 :         CATCH_REQUIRE(file->get_parameter("z") == "edge");
    1885           1 :         CATCH_REQUIRE(file->get_parameter("z::b") == "line");
    1886           1 :         CATCH_REQUIRE(file->get_parameter("z::b::c") == "12.72");
    1887           1 :     }
    1888           7 :     CATCH_END_SECTION()
    1889             : 
    1890           7 :     CATCH_START_SECTION("config_section_tests: section operator c++ (::)")
    1891             :     {
    1892           1 :         SNAP_CATCH2_NAMESPACE::init_tmp_dir("section-operator", "section-cpp");
    1893             : 
    1894             :         {
    1895           1 :             std::ofstream config_file;
    1896           1 :             config_file.open(SNAP_CATCH2_NAMESPACE::g_config_filename, std::ios_base::out | std::ios_base::binary | std::ios_base::trunc);
    1897           1 :             CATCH_REQUIRE(config_file.good());
    1898             :             config_file <<
    1899             :                 "# Auto-generated\n"
    1900             :                 "a=color\n"
    1901             :                 "a::b=red\n"
    1902             :                 "a::b::c=122\n"
    1903             :                 "m=size\n"
    1904             :                 "z=edge\n"
    1905             :                 "z::b=line\n"
    1906           1 :                 "z::b::c=12.72\n"
    1907             :             ;
    1908           1 :         }
    1909             : 
    1910           1 :         advgetopt::conf_file_setup setup(SNAP_CATCH2_NAMESPACE::g_config_filename
    1911             :                             , advgetopt::line_continuation_t::line_continuation_unix
    1912             :                             , advgetopt::ASSIGNMENT_OPERATOR_EQUAL
    1913             :                             , advgetopt::COMMENT_SHELL
    1914           1 :                             , advgetopt::SECTION_OPERATOR_CPP);
    1915             : 
    1916           1 :         CATCH_REQUIRE(setup.get_original_filename() == SNAP_CATCH2_NAMESPACE::g_config_filename);
    1917             : 
    1918           1 :         CATCH_REQUIRE(setup.is_valid());
    1919           1 :         CATCH_REQUIRE(setup.get_line_continuation() == advgetopt::line_continuation_t::line_continuation_unix);
    1920           1 :         CATCH_REQUIRE(setup.get_assignment_operator() == advgetopt::ASSIGNMENT_OPERATOR_EQUAL);
    1921           1 :         CATCH_REQUIRE(setup.get_comment() == advgetopt::COMMENT_SHELL);
    1922           1 :         CATCH_REQUIRE(setup.get_section_operator() == advgetopt::SECTION_OPERATOR_CPP);
    1923             : 
    1924           1 :         advgetopt::conf_file::pointer_t file(advgetopt::conf_file::get_conf_file(setup));
    1925             : 
    1926           1 :         CATCH_REQUIRE(file->get_setup().get_config_url() == setup.get_config_url());
    1927           1 :         CATCH_REQUIRE(file->get_errno() == 0);
    1928             : 
    1929           1 :         advgetopt::conf_file::sections_t sections(file->get_sections());
    1930           1 :         CATCH_REQUIRE(sections.size() == 4);
    1931           1 :         CATCH_REQUIRE(sections.find("a")    != sections.end());
    1932           1 :         CATCH_REQUIRE(sections.find("a::b") != sections.end());
    1933           1 :         CATCH_REQUIRE(sections.find("z")    != sections.end());
    1934           1 :         CATCH_REQUIRE(sections.find("z::b") != sections.end());
    1935             : 
    1936           1 :         CATCH_REQUIRE(file->get_parameters().size() == 7);
    1937             : 
    1938           1 :         CATCH_REQUIRE(file->has_parameter("a"));
    1939           1 :         CATCH_REQUIRE(file->has_parameter("a::b"));
    1940           1 :         CATCH_REQUIRE(file->has_parameter("a::b::c"));
    1941           1 :         CATCH_REQUIRE(file->has_parameter("m"));
    1942           1 :         CATCH_REQUIRE(file->has_parameter("z"));
    1943           1 :         CATCH_REQUIRE(file->has_parameter("z::b"));
    1944           1 :         CATCH_REQUIRE(file->has_parameter("z::b::c"));
    1945             : 
    1946           1 :         CATCH_REQUIRE(file->get_parameter("a") == "color");
    1947           1 :         CATCH_REQUIRE(file->get_parameter("a::b") == "red");
    1948           1 :         CATCH_REQUIRE(file->get_parameter("a::b::c") == "122");
    1949           1 :         CATCH_REQUIRE(file->get_parameter("m") == "size");
    1950           1 :         CATCH_REQUIRE(file->get_parameter("z") == "edge");
    1951           1 :         CATCH_REQUIRE(file->get_parameter("z::b") == "line");
    1952           1 :         CATCH_REQUIRE(file->get_parameter("z::b::c") == "12.72");
    1953           1 :     }
    1954           7 :     CATCH_END_SECTION()
    1955             : 
    1956           7 :     CATCH_START_SECTION("config_section_tests: section operator block ({ ... })")
    1957             :     {
    1958           1 :         SNAP_CATCH2_NAMESPACE::init_tmp_dir("section-operator", "section-block");
    1959             : 
    1960             :         {
    1961           1 :             std::ofstream config_file;
    1962           1 :             config_file.open(SNAP_CATCH2_NAMESPACE::g_config_filename, std::ios_base::out | std::ios_base::binary | std::ios_base::trunc);
    1963           1 :             CATCH_REQUIRE(config_file.good());
    1964             :             config_file <<
    1965             :                 "# Auto-generated\n"
    1966             :                 "a=color\n"
    1967             :                 "a {\n"
    1968             :                 "  b=red\n"
    1969             :                 "  b {\n"
    1970             :                 "    c=122\n"
    1971             :                 "  }\n"
    1972             :                 "}\n"
    1973             :                 "m=size\n"
    1974             :                 "z=edge\n"
    1975             :                 "z {\n"
    1976             :                 "  b {\n"
    1977             :                 "    c=12.72\n"
    1978             :                 "  }\n"
    1979             :                 "  b=line\n"
    1980           1 :                 "}\n"
    1981             :             ;
    1982           1 :         }
    1983             : 
    1984           1 :         advgetopt::conf_file_setup setup(SNAP_CATCH2_NAMESPACE::g_config_filename
    1985             :                             , advgetopt::line_continuation_t::line_continuation_unix
    1986             :                             , advgetopt::ASSIGNMENT_OPERATOR_EQUAL
    1987             :                             , advgetopt::COMMENT_SHELL
    1988           1 :                             , advgetopt::SECTION_OPERATOR_BLOCK);
    1989             : 
    1990           1 :         CATCH_REQUIRE(setup.get_original_filename() == SNAP_CATCH2_NAMESPACE::g_config_filename);
    1991             : 
    1992           1 :         CATCH_REQUIRE(setup.is_valid());
    1993           1 :         CATCH_REQUIRE(setup.get_line_continuation() == advgetopt::line_continuation_t::line_continuation_unix);
    1994           1 :         CATCH_REQUIRE(setup.get_assignment_operator() == advgetopt::ASSIGNMENT_OPERATOR_EQUAL);
    1995           1 :         CATCH_REQUIRE(setup.get_comment() == advgetopt::COMMENT_SHELL);
    1996           1 :         CATCH_REQUIRE(setup.get_section_operator() == advgetopt::SECTION_OPERATOR_BLOCK);
    1997             : 
    1998           1 :         advgetopt::conf_file::pointer_t file(advgetopt::conf_file::get_conf_file(setup));
    1999             : 
    2000           1 :         CATCH_REQUIRE(file->get_setup().get_config_url() == setup.get_config_url());
    2001           1 :         CATCH_REQUIRE(file->get_errno() == 0);
    2002             : 
    2003           1 :         advgetopt::conf_file::sections_t sections(file->get_sections());
    2004           1 :         CATCH_REQUIRE(sections.size() == 4);
    2005           1 :         CATCH_REQUIRE(sections.find("a")    != sections.end());
    2006           1 :         CATCH_REQUIRE(sections.find("a::b") != sections.end());
    2007           1 :         CATCH_REQUIRE(sections.find("z")    != sections.end());
    2008           1 :         CATCH_REQUIRE(sections.find("z::b") != sections.end());
    2009             : 
    2010           1 :         CATCH_REQUIRE(file->get_parameters().size() == 7);
    2011             : 
    2012           1 :         CATCH_REQUIRE(file->has_parameter("a"));
    2013           1 :         CATCH_REQUIRE(file->has_parameter("a::b"));
    2014           1 :         CATCH_REQUIRE(file->has_parameter("a::b::c"));
    2015           1 :         CATCH_REQUIRE(file->has_parameter("m"));
    2016           1 :         CATCH_REQUIRE(file->has_parameter("z"));
    2017           1 :         CATCH_REQUIRE(file->has_parameter("z::b"));
    2018           1 :         CATCH_REQUIRE(file->has_parameter("z::b::c"));
    2019             : 
    2020           1 :         CATCH_REQUIRE(file->get_parameter("a") == "color");
    2021           1 :         CATCH_REQUIRE(file->get_parameter("a::b") == "red");
    2022           1 :         CATCH_REQUIRE(file->get_parameter("a::b::c") == "122");
    2023           1 :         CATCH_REQUIRE(file->get_parameter("m") == "size");
    2024           1 :         CATCH_REQUIRE(file->get_parameter("z") == "edge");
    2025           1 :         CATCH_REQUIRE(file->get_parameter("z::b") == "line");
    2026           1 :         CATCH_REQUIRE(file->get_parameter("z::b::c") == "12.72");
    2027           1 :     }
    2028           7 :     CATCH_END_SECTION()
    2029             : 
    2030           7 :     CATCH_START_SECTION("config_section_tests: section operator ini file ([...])")
    2031             :     {
    2032           1 :         SNAP_CATCH2_NAMESPACE::init_tmp_dir("section-operator", "section-ini-file");
    2033             : 
    2034             :         {
    2035           1 :             std::ofstream config_file;
    2036           1 :             config_file.open(SNAP_CATCH2_NAMESPACE::g_config_filename, std::ios_base::out | std::ios_base::binary | std::ios_base::trunc);
    2037           1 :             CATCH_REQUIRE(config_file.good());
    2038             :             config_file <<
    2039             :                 "# Auto-generated\n"
    2040             :                 "a=color\n"
    2041             :                 "[a]\n"
    2042             :                 "b=red\n"
    2043             :                 "b-c=122\n"
    2044             :                 "[]\n"
    2045             :                 "m=size\n"
    2046             :                 "z=edge\n"
    2047             :                 "[z] # we allow comments here\n"
    2048             :                 "b=line\n"
    2049             :                 "b-c=12.72\n"
    2050             :                 "[p]#nospacenecessary\n"
    2051             :                 "b=comment\n"
    2052           1 :                 "b-c=allowed\n"
    2053             :             ;
    2054           1 :         }
    2055             : 
    2056           1 :         advgetopt::conf_file_setup setup(SNAP_CATCH2_NAMESPACE::g_config_filename
    2057             :                             , advgetopt::line_continuation_t::line_continuation_unix
    2058             :                             , advgetopt::ASSIGNMENT_OPERATOR_EQUAL
    2059             :                             , advgetopt::COMMENT_SHELL
    2060           1 :                             , advgetopt::SECTION_OPERATOR_INI_FILE);
    2061             : 
    2062           1 :         CATCH_REQUIRE(setup.get_original_filename() == SNAP_CATCH2_NAMESPACE::g_config_filename);
    2063             : 
    2064           1 :         CATCH_REQUIRE(setup.is_valid());
    2065           1 :         CATCH_REQUIRE(setup.get_line_continuation() == advgetopt::line_continuation_t::line_continuation_unix);
    2066           1 :         CATCH_REQUIRE(setup.get_assignment_operator() == advgetopt::ASSIGNMENT_OPERATOR_EQUAL);
    2067           1 :         CATCH_REQUIRE(setup.get_comment() == advgetopt::COMMENT_SHELL);
    2068           1 :         CATCH_REQUIRE(setup.get_section_operator() == advgetopt::SECTION_OPERATOR_INI_FILE);
    2069             : 
    2070           1 :         advgetopt::conf_file::pointer_t file(advgetopt::conf_file::get_conf_file(setup));
    2071             : 
    2072           1 :         CATCH_REQUIRE(file->get_setup().get_config_url() == setup.get_config_url());
    2073           1 :         CATCH_REQUIRE(file->get_errno() == 0);
    2074             : 
    2075           1 :         advgetopt::conf_file::sections_t sections(file->get_sections());
    2076           1 :         CATCH_REQUIRE(sections.size() == 3);
    2077           1 :         CATCH_REQUIRE(sections.find("a") != sections.end());
    2078           1 :         CATCH_REQUIRE(sections.find("z") != sections.end());
    2079           1 :         CATCH_REQUIRE(sections.find("p") != sections.end());
    2080             : 
    2081           1 :         CATCH_REQUIRE(file->get_parameters().size() == 9);
    2082             : 
    2083           1 :         CATCH_REQUIRE(file->has_parameter("a"));
    2084           1 :         CATCH_REQUIRE(file->has_parameter("a::b"));
    2085           1 :         CATCH_REQUIRE(file->has_parameter("a::b-c"));
    2086           1 :         CATCH_REQUIRE(file->has_parameter("m"));
    2087           1 :         CATCH_REQUIRE(file->has_parameter("z"));
    2088           1 :         CATCH_REQUIRE(file->has_parameter("z::b"));
    2089           1 :         CATCH_REQUIRE(file->has_parameter("z::b-c"));
    2090           1 :         CATCH_REQUIRE(file->has_parameter("p::b"));
    2091           1 :         CATCH_REQUIRE(file->has_parameter("p::b-c"));
    2092             : 
    2093           1 :         CATCH_REQUIRE(file->get_parameter("a") == "color");
    2094           1 :         CATCH_REQUIRE(file->get_parameter("a::b") == "red");
    2095           1 :         CATCH_REQUIRE(file->get_parameter("a::b-c") == "122");
    2096           1 :         CATCH_REQUIRE(file->get_parameter("m") == "size");
    2097           1 :         CATCH_REQUIRE(file->get_parameter("z") == "edge");
    2098           1 :         CATCH_REQUIRE(file->get_parameter("z::b") == "line");
    2099           1 :         CATCH_REQUIRE(file->get_parameter("z::b-c") == "12.72");
    2100           1 :         CATCH_REQUIRE(file->get_parameter("p::b") == "comment");
    2101           1 :         CATCH_REQUIRE(file->get_parameter("p::b-c") == "allowed");
    2102           1 :     }
    2103           7 :     CATCH_END_SECTION()
    2104             : 
    2105           7 :     CATCH_START_SECTION("config_section_tests: section operator ini-file & c++")
    2106             :     {
    2107           1 :         SNAP_CATCH2_NAMESPACE::init_tmp_dir("section-operator", "section-double");
    2108             : 
    2109             :         {
    2110           1 :             std::ofstream config_file;
    2111           1 :             config_file.open(SNAP_CATCH2_NAMESPACE::g_config_filename, std::ios_base::out | std::ios_base::binary | std::ios_base::trunc);
    2112           1 :             CATCH_REQUIRE(config_file.good());
    2113             :             config_file <<
    2114             :                 "# Auto-generated\n"
    2115             :                 "[a]\n"
    2116             :                 "b=red\n"
    2117             :                 "b::c=209\n"
    2118             :                 "::h=high\n"
    2119             :                 "m=size\n"
    2120             :                 "[z]\n"
    2121             :                 "z=edge\n"
    2122             :                 "::b=line\n"
    2123           1 :                 "z::b::c=17.92\n"
    2124             :             ;
    2125           1 :         }
    2126             : 
    2127           1 :         advgetopt::conf_file_setup setup(SNAP_CATCH2_NAMESPACE::g_config_filename
    2128             :                             , advgetopt::line_continuation_t::line_continuation_unix
    2129             :                             , advgetopt::ASSIGNMENT_OPERATOR_EQUAL
    2130             :                             , advgetopt::COMMENT_SHELL
    2131           1 :                             , advgetopt::SECTION_OPERATOR_INI_FILE | advgetopt::SECTION_OPERATOR_CPP);
    2132             : 
    2133           1 :         CATCH_REQUIRE(setup.get_original_filename() == SNAP_CATCH2_NAMESPACE::g_config_filename);
    2134             : 
    2135           1 :         CATCH_REQUIRE(setup.is_valid());
    2136           1 :         CATCH_REQUIRE(setup.get_line_continuation() == advgetopt::line_continuation_t::line_continuation_unix);
    2137           1 :         CATCH_REQUIRE(setup.get_assignment_operator() == advgetopt::ASSIGNMENT_OPERATOR_EQUAL);
    2138           1 :         CATCH_REQUIRE(setup.get_comment() == advgetopt::COMMENT_SHELL);
    2139           1 :         CATCH_REQUIRE(setup.get_section_operator() == (advgetopt::SECTION_OPERATOR_INI_FILE | advgetopt::SECTION_OPERATOR_CPP));
    2140             : 
    2141           1 :         advgetopt::conf_file::pointer_t file(advgetopt::conf_file::get_conf_file(setup));
    2142             : 
    2143           1 :         CATCH_REQUIRE(file->get_setup().get_config_url() == setup.get_config_url());
    2144           1 :         CATCH_REQUIRE(file->get_errno() == 0);
    2145             : 
    2146           1 :         advgetopt::conf_file::sections_t sections(file->get_sections());
    2147           1 :         CATCH_REQUIRE(sections.size() == 4);
    2148           1 :         CATCH_REQUIRE(sections.find("a")       != sections.end());
    2149           1 :         CATCH_REQUIRE(sections.find("a::b")    != sections.end());
    2150           1 :         CATCH_REQUIRE(sections.find("z")       != sections.end());
    2151           1 :         CATCH_REQUIRE(sections.find("z::z::b") != sections.end());
    2152             : 
    2153           1 :         CATCH_REQUIRE(file->get_parameters().size() == 7);
    2154             : 
    2155           1 :         CATCH_REQUIRE(file->has_parameter("a::b"));
    2156           1 :         CATCH_REQUIRE(file->has_parameter("a::b::c"));
    2157           1 :         CATCH_REQUIRE(file->has_parameter("h"));
    2158           1 :         CATCH_REQUIRE(file->has_parameter("a::m"));
    2159           1 :         CATCH_REQUIRE(file->has_parameter("z::z"));
    2160           1 :         CATCH_REQUIRE(file->has_parameter("b"));
    2161           1 :         CATCH_REQUIRE(file->has_parameter("z::z::b::c"));
    2162             : 
    2163           1 :         CATCH_REQUIRE(file->get_parameter("a::b") == "red");
    2164           1 :         CATCH_REQUIRE(file->get_parameter("a::b::c") == "209");
    2165           1 :         CATCH_REQUIRE(file->get_parameter("h") == "high");
    2166           1 :         CATCH_REQUIRE(file->get_parameter("a::m") == "size");
    2167           1 :         CATCH_REQUIRE(file->get_parameter("z::z") == "edge");
    2168           1 :         CATCH_REQUIRE(file->get_parameter("b") == "line");
    2169           1 :         CATCH_REQUIRE(file->get_parameter("z::z::b::c") == "17.92");
    2170           1 :     }
    2171           7 :     CATCH_END_SECTION()
    2172             : 
    2173           7 :     CATCH_START_SECTION("config_section_tests: section of variables ([variables])")
    2174             :     {
    2175             :         // in a config file variables are not auto-managed
    2176             :         //
    2177           1 :         SNAP_CATCH2_NAMESPACE::init_tmp_dir("section-variables", "section-with-variables");
    2178             : 
    2179             :         {
    2180           1 :             std::ofstream config_file;
    2181           1 :             config_file.open(SNAP_CATCH2_NAMESPACE::g_config_filename, std::ios_base::out | std::ios_base::binary | std::ios_base::trunc);
    2182           1 :             CATCH_REQUIRE(config_file.good());
    2183             :             config_file <<
    2184             :                 "# Auto-generated\n"
    2185             :                 "edge=${blue}\n"
    2186             :                 "background=${white}\n"
    2187             :                 "foreground=${black}\n"
    2188             :                 "[error]\n"
    2189             :                 "edge=${red}\n"
    2190             :                 "background=${gray}\n"
    2191             :                 "[variables]\n"
    2192             :                 "red=\"#ff0000\"\n"
    2193             :                 "green=\"#00ff00\"\n"
    2194             :                 "blue=\"#0000ff\"\n"
    2195             :                 "no_color=\"#000000\"\n"
    2196             :                 "black=${no_color}\n"
    2197             :                 "white=\"#ffffff\"\n"
    2198           1 :                 "gray=\"#aaaaaa\"\n"
    2199             :             ;
    2200           1 :         }
    2201             : 
    2202           1 :         advgetopt::conf_file_setup setup(SNAP_CATCH2_NAMESPACE::g_config_filename
    2203             :                             , advgetopt::line_continuation_t::line_continuation_unix
    2204             :                             , advgetopt::ASSIGNMENT_OPERATOR_EQUAL
    2205             :                             , advgetopt::COMMENT_SHELL
    2206           1 :                             , advgetopt::SECTION_OPERATOR_INI_FILE);
    2207             : 
    2208           1 :         CATCH_REQUIRE(setup.get_original_filename() == SNAP_CATCH2_NAMESPACE::g_config_filename);
    2209             : 
    2210           1 :         CATCH_REQUIRE(setup.is_valid());
    2211           1 :         CATCH_REQUIRE(setup.get_line_continuation() == advgetopt::line_continuation_t::line_continuation_unix);
    2212           1 :         CATCH_REQUIRE(setup.get_assignment_operator() == advgetopt::ASSIGNMENT_OPERATOR_EQUAL);
    2213           1 :         CATCH_REQUIRE(setup.get_comment() == advgetopt::COMMENT_SHELL);
    2214           1 :         CATCH_REQUIRE(setup.get_section_operator() == advgetopt::SECTION_OPERATOR_INI_FILE);
    2215             : 
    2216           1 :         advgetopt::conf_file::pointer_t file(advgetopt::conf_file::get_conf_file(setup));
    2217             : 
    2218           1 :         CATCH_REQUIRE(file->get_setup().get_config_url() == setup.get_config_url());
    2219           1 :         CATCH_REQUIRE(file->get_errno() == 0);
    2220             : 
    2221           1 :         advgetopt::conf_file::sections_t sections(file->get_sections());
    2222           1 :         CATCH_REQUIRE(sections.size() == 2);
    2223           1 :         CATCH_REQUIRE(sections.find("error") != sections.end());
    2224           1 :         CATCH_REQUIRE(sections.find("variables") != sections.end());
    2225             : 
    2226           1 :         CATCH_REQUIRE(file->get_parameters().size() == 12);
    2227             : 
    2228           1 :         CATCH_REQUIRE(file->has_parameter("edge"));
    2229           1 :         CATCH_REQUIRE(file->has_parameter("background"));
    2230           1 :         CATCH_REQUIRE(file->has_parameter("foreground"));
    2231           1 :         CATCH_REQUIRE(file->has_parameter("error::edge"));
    2232           1 :         CATCH_REQUIRE(file->has_parameter("error::background"));
    2233           1 :         CATCH_REQUIRE(file->has_parameter("variables::red"));
    2234           1 :         CATCH_REQUIRE(file->has_parameter("variables::green"));
    2235           1 :         CATCH_REQUIRE(file->has_parameter("variables::blue"));
    2236           1 :         CATCH_REQUIRE(file->has_parameter("variables::no_color"));
    2237           1 :         CATCH_REQUIRE(file->has_parameter("variables::black"));
    2238           1 :         CATCH_REQUIRE(file->has_parameter("variables::white"));
    2239           1 :         CATCH_REQUIRE(file->has_parameter("variables::gray"));
    2240             : 
    2241             :         // without a variables attached, we get the raw (original) data back
    2242             :         //
    2243           1 :         CATCH_REQUIRE(file->get_parameter("edge") == "${blue}");
    2244           1 :         CATCH_REQUIRE(file->get_parameter("background") == "${white}");
    2245           1 :         CATCH_REQUIRE(file->get_parameter("foreground") == "${black}");
    2246           1 :         CATCH_REQUIRE(file->get_parameter("error::edge") == "${red}");
    2247           1 :         CATCH_REQUIRE(file->get_parameter("error::background") == "${gray}");
    2248           1 :         CATCH_REQUIRE(file->get_parameter("variables::red") == "#ff0000");
    2249           1 :         CATCH_REQUIRE(file->get_parameter("variables::green") == "#00ff00");
    2250           1 :         CATCH_REQUIRE(file->get_parameter("variables::blue") == "#0000ff");
    2251           1 :         CATCH_REQUIRE(file->get_parameter("variables::no_color") == "#000000");
    2252           1 :         CATCH_REQUIRE(file->get_parameter("variables::black") == "${no_color}");
    2253           1 :         CATCH_REQUIRE(file->get_parameter("variables::white") == "#ffffff");
    2254           1 :         CATCH_REQUIRE(file->get_parameter("variables::gray") == "#aaaaaa");
    2255             : 
    2256             :         // transform the "[variables]" section to variables
    2257             :         //
    2258           1 :         advgetopt::variables::pointer_t vars(std::make_shared<advgetopt::variables>());
    2259           1 :         CATCH_REQUIRE(file->section_to_variables("variables", vars) == 7);
    2260           1 :         file->set_variables(vars);
    2261           1 :         CATCH_REQUIRE(file->get_variables() == vars);
    2262             : 
    2263           1 :         sections = file->get_sections();
    2264           1 :         CATCH_REQUIRE(sections.size() == 1);
    2265           1 :         CATCH_REQUIRE(sections.find("error") != sections.end());
    2266           1 :         CATCH_REQUIRE(sections.find("variables") == sections.end());
    2267             : 
    2268           1 :         CATCH_REQUIRE(file->get_parameters().size() == 5);
    2269             : 
    2270           1 :         CATCH_REQUIRE(file->has_parameter("edge"));
    2271           1 :         CATCH_REQUIRE(file->has_parameter("background"));
    2272           1 :         CATCH_REQUIRE(file->has_parameter("foreground"));
    2273           1 :         CATCH_REQUIRE(file->has_parameter("error::edge"));
    2274           1 :         CATCH_REQUIRE(file->has_parameter("error::background"));
    2275           1 :         CATCH_REQUIRE_FALSE(file->has_parameter("variables::red"));
    2276           1 :         CATCH_REQUIRE_FALSE(file->has_parameter("variables::green"));
    2277           1 :         CATCH_REQUIRE_FALSE(file->has_parameter("variables::blue"));
    2278           1 :         CATCH_REQUIRE_FALSE(file->has_parameter("variables::no_color"));
    2279           1 :         CATCH_REQUIRE_FALSE(file->has_parameter("variables::black"));
    2280           1 :         CATCH_REQUIRE_FALSE(file->has_parameter("variables::white"));
    2281           1 :         CATCH_REQUIRE_FALSE(file->has_parameter("variables::gray"));
    2282             : 
    2283           1 :         CATCH_REQUIRE(file->get_parameter("edge") == "#0000ff");
    2284           1 :         CATCH_REQUIRE(file->get_parameter("background") == "#ffffff");
    2285           1 :         CATCH_REQUIRE(file->get_parameter("foreground") == "#000000");
    2286           1 :         CATCH_REQUIRE(file->get_parameter("error::edge") == "#ff0000");
    2287           1 :         CATCH_REQUIRE(file->get_parameter("error::background") == "#aaaaaa");
    2288           1 :     }
    2289           7 :     CATCH_END_SECTION()
    2290             : 
    2291           7 :     CATCH_START_SECTION("command line with .conf including section of variables ([variables])")
    2292             :     {
    2293             :         // in a config file variables are not auto-managed
    2294             :         //
    2295           1 :         SNAP_CATCH2_NAMESPACE::init_tmp_dir("command-line-and-section-variables", "command-section-with-variables");
    2296             : 
    2297             :         {
    2298           1 :             std::ofstream config_file;
    2299           1 :             config_file.open(SNAP_CATCH2_NAMESPACE::g_config_filename, std::ios_base::out | std::ios_base::binary | std::ios_base::trunc);
    2300           1 :             CATCH_REQUIRE(config_file.good());
    2301             :             config_file <<
    2302             :                 "# Auto-generated\n"
    2303             :                 "edge=${blue}\n"
    2304             :                 "background=${white}\n"
    2305             :                 "foreground=${black}\n"
    2306             :                 "[error]\n"
    2307             :                 "edge=${red}\n"
    2308             :                 "background=${gray}\n"
    2309             :                 "[variables]\n"
    2310             :                 "red=\"#ff0000\"\n"
    2311             :                 "green=\"#00ff00\"\n"
    2312             :                 "blue=\"#0000ff\"\n"
    2313             :                 "no_color=\"#000000\"\n"
    2314             :                 "black=${no_color}\n"
    2315             :                 "orange=\"#80ff00\"\n"
    2316             :                 "white=\"#ffffff\"\n"
    2317           1 :                 "gray=\"#aaaaaa\"\n"
    2318             :             ;
    2319           1 :         }
    2320             : 
    2321           1 :         const advgetopt::option options[] =
    2322             :         {
    2323             :             advgetopt::define_option(
    2324             :                   advgetopt::Name("edge")
    2325             :                 , advgetopt::Flags(advgetopt::all_flags<
    2326             :                       advgetopt::GETOPT_FLAG_REQUIRED
    2327             :                     , advgetopt::GETOPT_FLAG_PROCESS_VARIABLES>())
    2328             :             ),
    2329             :             advgetopt::define_option(
    2330             :                   advgetopt::Name("background")
    2331             :                 , advgetopt::Flags(advgetopt::all_flags<
    2332             :                       advgetopt::GETOPT_FLAG_REQUIRED
    2333             :                     , advgetopt::GETOPT_FLAG_PROCESS_VARIABLES>())
    2334             :             ),
    2335             :             advgetopt::define_option(
    2336             :                   advgetopt::Name("foreground")
    2337             :                 , advgetopt::Flags(advgetopt::all_flags<
    2338             :                       advgetopt::GETOPT_FLAG_REQUIRED
    2339             :                     , advgetopt::GETOPT_FLAG_PROCESS_VARIABLES>())
    2340             :             ),
    2341             :             advgetopt::define_option(
    2342             :                   advgetopt::Name("error::edge")
    2343             :                 , advgetopt::Flags(advgetopt::all_flags<
    2344             :                       advgetopt::GETOPT_FLAG_REQUIRED
    2345             :                     , advgetopt::GETOPT_FLAG_PROCESS_VARIABLES>())
    2346             :             ),
    2347             :             advgetopt::define_option(
    2348             :                   advgetopt::Name("error::background")
    2349             :                 , advgetopt::Flags(advgetopt::all_flags<
    2350             :                       advgetopt::GETOPT_FLAG_REQUIRED
    2351             :                     , advgetopt::GETOPT_FLAG_PROCESS_VARIABLES>())
    2352             :             ),
    2353             :             advgetopt::define_option(
    2354             :                   advgetopt::Name("error::foreground")
    2355             :                 , advgetopt::Flags(advgetopt::all_flags<
    2356             :                       advgetopt::GETOPT_FLAG_REQUIRED
    2357             :                     , advgetopt::GETOPT_FLAG_PROCESS_VARIABLES>())
    2358             :             ),
    2359             :             advgetopt::define_option(
    2360             :                   advgetopt::Name("see-config")
    2361             :                 , advgetopt::Flags(advgetopt::standalone_command_flags<>())
    2362             :             ),
    2363             :             advgetopt::end_options()
    2364             :         };
    2365             : 
    2366           1 :         char const * const configuration_files[] =
    2367             :         {
    2368           1 :             SNAP_CATCH2_NAMESPACE::g_config_filename.c_str(),
    2369             :             nullptr
    2370           1 :         };
    2371             : 
    2372           1 :         advgetopt::options_environment environment_options;
    2373           1 :         environment_options.f_project_name = "unittest";
    2374           1 :         environment_options.f_options = options;
    2375           1 :         environment_options.f_help_header = "Usage: configuration with variables through environment.";
    2376           1 :         environment_options.f_section_variables_name = "variables";
    2377           1 :         environment_options.f_configuration_files = configuration_files;
    2378             : 
    2379           1 :         char const * cargv[] =
    2380             :         {
    2381             :             "/usr/bin/cmd-n-config",
    2382             :             "--see-config",
    2383             :             "--error::foreground",
    2384             :             "${orange}",
    2385             :             nullptr
    2386             :         };
    2387           1 :         int const argc(sizeof(cargv) / sizeof(cargv[0]) - 1);
    2388           1 :         char ** argv = const_cast<char **>(cargv);
    2389             : 
    2390           1 :         advgetopt::getopt::pointer_t opts(std::make_shared<advgetopt::getopt>(environment_options, argc, argv));
    2391           1 :         CATCH_REQUIRE(opts != nullptr);
    2392             : 
    2393           1 :         advgetopt::variables::pointer_t variables(opts->get_variables());
    2394           1 :         CATCH_REQUIRE(variables != nullptr);
    2395             : 
    2396           1 :         CATCH_REQUIRE(opts->is_defined("see-config"));
    2397           1 :         CATCH_REQUIRE(opts->is_defined("edge"));
    2398           1 :         CATCH_REQUIRE(opts->is_defined("background"));
    2399           1 :         CATCH_REQUIRE(opts->is_defined("foreground"));
    2400           1 :         CATCH_REQUIRE(opts->is_defined("error::edge"));
    2401           1 :         CATCH_REQUIRE(opts->is_defined("error::background"));
    2402           1 :         CATCH_REQUIRE(opts->is_defined("error::foreground"));
    2403           1 :         CATCH_REQUIRE_FALSE(opts->is_defined("variables::red"));
    2404           1 :         CATCH_REQUIRE_FALSE(opts->is_defined("variables::green"));
    2405           1 :         CATCH_REQUIRE_FALSE(opts->is_defined("variables::blue"));
    2406           1 :         CATCH_REQUIRE_FALSE(opts->is_defined("variables::no_color"));
    2407           1 :         CATCH_REQUIRE_FALSE(opts->is_defined("variables::black"));
    2408           1 :         CATCH_REQUIRE_FALSE(opts->is_defined("variables::orange"));
    2409           1 :         CATCH_REQUIRE_FALSE(opts->is_defined("variables::white"));
    2410           1 :         CATCH_REQUIRE_FALSE(opts->is_defined("variables::gray"));
    2411             : 
    2412           1 :         CATCH_REQUIRE(opts->get_string("edge") == "#0000ff");
    2413           1 :         CATCH_REQUIRE(opts->get_string("background") == "#ffffff");
    2414           1 :         CATCH_REQUIRE(opts->get_string("foreground") == "#000000");
    2415           1 :         CATCH_REQUIRE(opts->get_string("error::edge") == "#ff0000");
    2416           1 :         CATCH_REQUIRE(opts->get_string("error::background") == "#aaaaaa");
    2417           1 :         CATCH_REQUIRE(opts->get_string("error::foreground") == "#80ff00");
    2418             : 
    2419           1 :         CATCH_REQUIRE(opts->get_option("edge")->get_variables() == variables);
    2420           1 :         CATCH_REQUIRE(opts->get_option("background")->get_variables() == variables);
    2421           1 :         CATCH_REQUIRE(opts->get_option("foreground")->get_variables() == variables);
    2422           1 :         CATCH_REQUIRE(opts->get_option("error::edge")->get_variables() == variables);
    2423           1 :         CATCH_REQUIRE(opts->get_option("error::background")->get_variables() == variables);
    2424           1 :         CATCH_REQUIRE(opts->get_option("error::foreground")->get_variables() == variables);
    2425           1 :     }
    2426           7 :     CATCH_END_SECTION()
    2427           7 : }
    2428             : 
    2429             : 
    2430             : 
    2431             : 
    2432           3 : CATCH_TEST_CASE("save_config_file", "[config][getopt][valid]")
    2433             : {
    2434           3 :     CATCH_START_SECTION("save_config_file: load update save (=)")
    2435             :     {
    2436           1 :         SNAP_CATCH2_NAMESPACE::init_tmp_dir("save-operation", "configuration-equal");
    2437             : 
    2438             :         {
    2439           1 :             std::ofstream config_file;
    2440           1 :             config_file.open(SNAP_CATCH2_NAMESPACE::g_config_filename, std::ios_base::out | std::ios_base::binary | std::ios_base::trunc);
    2441           1 :             CATCH_REQUIRE(config_file.good());
    2442             :             config_file <<
    2443             :                 "# Auto-generated\n"
    2444             :                 "a=color\n"
    2445             :                 "b=red\n"
    2446           1 :                 "call-flag=122\n"
    2447             :             ;
    2448           1 :         }
    2449             : 
    2450           1 :         advgetopt::conf_file_setup setup(SNAP_CATCH2_NAMESPACE::g_config_filename
    2451             :                             , advgetopt::line_continuation_t::line_continuation_single_line
    2452             :                             , advgetopt::ASSIGNMENT_OPERATOR_EQUAL
    2453             :                             , advgetopt::COMMENT_SHELL
    2454           1 :                             , advgetopt::SECTION_OPERATOR_NONE);
    2455             : 
    2456           1 :         CATCH_REQUIRE(setup.get_original_filename() == SNAP_CATCH2_NAMESPACE::g_config_filename);
    2457             : 
    2458           1 :         CATCH_REQUIRE(setup.is_valid());
    2459           1 :         CATCH_REQUIRE(setup.get_line_continuation() == advgetopt::line_continuation_t::line_continuation_single_line);
    2460           1 :         CATCH_REQUIRE(setup.get_assignment_operator() == advgetopt::ASSIGNMENT_OPERATOR_EQUAL);
    2461           1 :         CATCH_REQUIRE(setup.get_comment() == advgetopt::COMMENT_SHELL);
    2462           1 :         CATCH_REQUIRE(setup.get_section_operator() == advgetopt::SECTION_OPERATOR_NONE);
    2463             : 
    2464           1 :         advgetopt::conf_file::pointer_t file(advgetopt::conf_file::get_conf_file(setup));
    2465             : 
    2466           1 :         CATCH_REQUIRE(file->exists());
    2467           1 :         CATCH_REQUIRE(file->get_setup().get_config_url() == setup.get_config_url());
    2468           1 :         CATCH_REQUIRE(file->get_errno() == 0);
    2469             : 
    2470           1 :         advgetopt::conf_file::sections_t sections(file->get_sections());
    2471           1 :         CATCH_REQUIRE(sections.empty());
    2472             : 
    2473           1 :         CATCH_REQUIRE(file->get_parameters().size() == 3);
    2474             : 
    2475           1 :         CATCH_REQUIRE(file->has_parameter("a"));
    2476           1 :         CATCH_REQUIRE(file->has_parameter("b"));
    2477           1 :         CATCH_REQUIRE(file->has_parameter("call-flag"));
    2478             : 
    2479           1 :         CATCH_REQUIRE(file->get_parameter("a") == "color");
    2480           1 :         CATCH_REQUIRE(file->get_parameter("b") == "red");
    2481           1 :         CATCH_REQUIRE(file->get_parameter("call-flag") == "122");
    2482             : 
    2483           1 :         CATCH_REQUIRE(file->save_configuration());
    2484             : 
    2485             :         // no backup since there was no modification so the save did nothing
    2486             :         //
    2487           1 :         CATCH_REQUIRE(access((SNAP_CATCH2_NAMESPACE::g_config_filename + ".bak").c_str(), F_OK) != 0);
    2488             : 
    2489           1 :         file->set_parameter(std::string(), "a", "size");
    2490           1 :         file->set_parameter(std::string(), "b", "tall");
    2491           1 :         file->set_parameter(std::string(), "call-flag", "1920");
    2492             : 
    2493           1 :         CATCH_REQUIRE(file->save_configuration());
    2494             : 
    2495           1 :         CATCH_REQUIRE(access((SNAP_CATCH2_NAMESPACE::g_config_filename + ".bak").c_str(), F_OK) == 0);
    2496             : 
    2497           1 :         file->set_parameter(std::string(), "a", "pace");
    2498           1 :         file->set_parameter(std::string(), "b", "fall");
    2499           1 :         file->set_parameter(std::string(), "call-flag", "2019");
    2500             : 
    2501           1 :         CATCH_REQUIRE(file->save_configuration("save"));
    2502             : 
    2503           1 :         CATCH_REQUIRE(access((SNAP_CATCH2_NAMESPACE::g_config_filename + ".save").c_str(), F_OK) == 0);
    2504             : 
    2505           1 :         std::string const new_name(SNAP_CATCH2_NAMESPACE::g_config_filename + ".conf2");
    2506           1 :         rename(SNAP_CATCH2_NAMESPACE::g_config_filename.c_str(), new_name.c_str());
    2507             : 
    2508           1 :         advgetopt::conf_file_setup setup2(new_name
    2509             :                             , advgetopt::line_continuation_t::line_continuation_single_line
    2510             :                             , advgetopt::ASSIGNMENT_OPERATOR_EQUAL
    2511             :                             , advgetopt::COMMENT_SHELL
    2512           1 :                             , advgetopt::SECTION_OPERATOR_NONE);
    2513             : 
    2514           1 :         CATCH_REQUIRE(setup.get_original_filename() == SNAP_CATCH2_NAMESPACE::g_config_filename);
    2515             : 
    2516           1 :         CATCH_REQUIRE(setup2.is_valid());
    2517           1 :         CATCH_REQUIRE(setup2.get_line_continuation() == advgetopt::line_continuation_t::line_continuation_single_line);
    2518           1 :         CATCH_REQUIRE(setup2.get_assignment_operator() == advgetopt::ASSIGNMENT_OPERATOR_EQUAL);
    2519           1 :         CATCH_REQUIRE(setup2.get_comment() == advgetopt::COMMENT_SHELL);
    2520           1 :         CATCH_REQUIRE(setup2.get_section_operator() == advgetopt::SECTION_OPERATOR_NONE);
    2521             : 
    2522           1 :         advgetopt::conf_file::pointer_t file2(advgetopt::conf_file::get_conf_file(setup2));
    2523             : 
    2524           1 :         CATCH_REQUIRE(file2->exists());
    2525           1 :         CATCH_REQUIRE(file2->get_setup().get_config_url() == setup2.get_config_url());
    2526           1 :         CATCH_REQUIRE(file2->get_errno() == 0);
    2527             : 
    2528           1 :         CATCH_REQUIRE(file->get_sections().empty());
    2529             : 
    2530           1 :         CATCH_REQUIRE(file2->get_parameters().size() == 3);
    2531             : 
    2532           1 :         CATCH_REQUIRE(file2->has_parameter("a"));
    2533           1 :         CATCH_REQUIRE(file2->has_parameter("b"));
    2534           1 :         CATCH_REQUIRE(file2->has_parameter("call-flag"));
    2535             : 
    2536           1 :         CATCH_REQUIRE(file2->get_parameter("a") == "pace");
    2537           1 :         CATCH_REQUIRE(file2->get_parameter("b") == "fall");
    2538           1 :         CATCH_REQUIRE(file2->get_parameter("call-flag") == "2019");
    2539           1 :     }
    2540           3 :     CATCH_END_SECTION()
    2541             : 
    2542           3 :     CATCH_START_SECTION("save_config_file: load update save (:)")
    2543             :     {
    2544           1 :         SNAP_CATCH2_NAMESPACE::init_tmp_dir("save-operation", "configuration-colon");
    2545             : 
    2546             :         {
    2547           1 :             std::ofstream config_file;
    2548           1 :             config_file.open(SNAP_CATCH2_NAMESPACE::g_config_filename, std::ios_base::out | std::ios_base::binary | std::ios_base::trunc);
    2549           1 :             CATCH_REQUIRE(config_file.good());
    2550             :             config_file <<
    2551             :                 "# Auto-generated\n"
    2552             :                 "a: color\n"
    2553             :                 "b: red\n"
    2554           1 :                 "call-flag: 122\n"
    2555             :             ;
    2556           1 :         }
    2557             : 
    2558           1 :         advgetopt::conf_file_setup setup(SNAP_CATCH2_NAMESPACE::g_config_filename
    2559             :                             , advgetopt::line_continuation_t::line_continuation_single_line
    2560             :                             , advgetopt::ASSIGNMENT_OPERATOR_COLON
    2561             :                             , advgetopt::COMMENT_SHELL
    2562             :                             , advgetopt::SECTION_OPERATOR_NONE
    2563           1 :                             , advgetopt::NAME_SEPARATOR_DASHES);
    2564             : 
    2565           1 :         CATCH_REQUIRE(setup.get_original_filename() == SNAP_CATCH2_NAMESPACE::g_config_filename);
    2566             : 
    2567           1 :         CATCH_REQUIRE(setup.is_valid());
    2568           1 :         CATCH_REQUIRE(setup.get_line_continuation() == advgetopt::line_continuation_t::line_continuation_single_line);
    2569           1 :         CATCH_REQUIRE(setup.get_assignment_operator() == advgetopt::ASSIGNMENT_OPERATOR_COLON);
    2570           1 :         CATCH_REQUIRE(setup.get_comment() == advgetopt::COMMENT_SHELL);
    2571           1 :         CATCH_REQUIRE(setup.get_section_operator() == advgetopt::SECTION_OPERATOR_NONE);
    2572             : 
    2573           1 :         advgetopt::conf_file::pointer_t file(advgetopt::conf_file::get_conf_file(setup));
    2574             : 
    2575           1 :         CATCH_REQUIRE(file->exists());
    2576           1 :         CATCH_REQUIRE(file->get_setup().get_config_url() == setup.get_config_url());
    2577           1 :         CATCH_REQUIRE(file->get_errno() == 0);
    2578             : 
    2579           1 :         advgetopt::conf_file::sections_t sections(file->get_sections());
    2580           1 :         CATCH_REQUIRE(sections.empty());
    2581             : 
    2582           1 :         CATCH_REQUIRE(file->get_parameters().size() == 3);
    2583             : 
    2584           1 :         CATCH_REQUIRE(file->has_parameter("a"));
    2585           1 :         CATCH_REQUIRE(file->has_parameter("b"));
    2586           1 :         CATCH_REQUIRE(file->has_parameter("call-flag"));
    2587             : 
    2588           1 :         CATCH_REQUIRE(file->get_parameter("a") == "color");
    2589           1 :         CATCH_REQUIRE(file->get_parameter("b") == "red");
    2590           1 :         CATCH_REQUIRE(file->get_parameter("call-flag") == "122");
    2591             : 
    2592           1 :         CATCH_REQUIRE(file->save_configuration());
    2593             : 
    2594             :         // no backup since there was no modification so the save did nothing
    2595             :         //
    2596           1 :         CATCH_REQUIRE(access((SNAP_CATCH2_NAMESPACE::g_config_filename + ".bak").c_str(), F_OK) != 0);
    2597             : 
    2598           1 :         file->set_parameter(std::string(), "a", "size");
    2599           1 :         file->set_parameter(std::string(), "b", "tall");
    2600           1 :         file->set_parameter(std::string(), "call-flag", "1920");
    2601             : 
    2602           1 :         CATCH_REQUIRE(file->save_configuration());
    2603             : 
    2604           1 :         CATCH_REQUIRE(access((SNAP_CATCH2_NAMESPACE::g_config_filename + ".bak").c_str(), F_OK) == 0);
    2605             : 
    2606           1 :         file->set_parameter(std::string(), "a", "pace");
    2607           1 :         file->set_parameter(std::string(), "b", "fall");
    2608           1 :         file->set_parameter(std::string(), "call-flag", "2019");
    2609             : 
    2610           1 :         CATCH_REQUIRE(file->save_configuration("save"));
    2611             : 
    2612           1 :         CATCH_REQUIRE(access((SNAP_CATCH2_NAMESPACE::g_config_filename + ".save").c_str(), F_OK) == 0);
    2613             : 
    2614           1 :         std::string const new_name(SNAP_CATCH2_NAMESPACE::g_config_filename + ".conf2");
    2615           1 :         rename(SNAP_CATCH2_NAMESPACE::g_config_filename.c_str(), new_name.c_str());
    2616             : 
    2617           1 :         advgetopt::conf_file_setup setup2(new_name
    2618             :                             , advgetopt::line_continuation_t::line_continuation_single_line
    2619             :                             , advgetopt::ASSIGNMENT_OPERATOR_COLON
    2620             :                             , advgetopt::COMMENT_SHELL
    2621             :                             , advgetopt::SECTION_OPERATOR_NONE
    2622           1 :                             , advgetopt::NAME_SEPARATOR_DASHES);
    2623             : 
    2624           1 :         CATCH_REQUIRE(setup.get_original_filename() == SNAP_CATCH2_NAMESPACE::g_config_filename);
    2625             : 
    2626           1 :         CATCH_REQUIRE(setup2.is_valid());
    2627           1 :         CATCH_REQUIRE(setup2.get_line_continuation() == advgetopt::line_continuation_t::line_continuation_single_line);
    2628           1 :         CATCH_REQUIRE(setup2.get_assignment_operator() == advgetopt::ASSIGNMENT_OPERATOR_COLON);
    2629           1 :         CATCH_REQUIRE(setup2.get_comment() == advgetopt::COMMENT_SHELL);
    2630           1 :         CATCH_REQUIRE(setup2.get_section_operator() == advgetopt::SECTION_OPERATOR_NONE);
    2631             : 
    2632           1 :         advgetopt::conf_file::pointer_t file2(advgetopt::conf_file::get_conf_file(setup2));
    2633             : 
    2634           1 :         CATCH_REQUIRE(file2->exists());
    2635           1 :         CATCH_REQUIRE(file2->get_setup().get_config_url() == setup2.get_config_url());
    2636           1 :         CATCH_REQUIRE(file2->get_errno() == 0);
    2637             : 
    2638           1 :         CATCH_REQUIRE(file->get_sections().empty());
    2639             : 
    2640           1 :         CATCH_REQUIRE(file2->get_parameters().size() == 3);
    2641             : 
    2642           1 :         CATCH_REQUIRE(file2->has_parameter("a"));
    2643           1 :         CATCH_REQUIRE(file2->has_parameter("b"));
    2644           1 :         CATCH_REQUIRE(file2->has_parameter("call-flag"));
    2645             : 
    2646           1 :         CATCH_REQUIRE(file2->get_parameter("a") == "pace");
    2647           1 :         CATCH_REQUIRE(file2->get_parameter("b") == "fall");
    2648           1 :         CATCH_REQUIRE(file2->get_parameter("call-flag") == "2019");
    2649           1 :     }
    2650           3 :     CATCH_END_SECTION()
    2651             : 
    2652           3 :     CATCH_START_SECTION("save_config_file: load update save ( )")
    2653             :     {
    2654           1 :         SNAP_CATCH2_NAMESPACE::init_tmp_dir("save-operation", "configuration-space");
    2655             : 
    2656             :         {
    2657           1 :             std::ofstream config_file;
    2658           1 :             config_file.open(SNAP_CATCH2_NAMESPACE::g_config_filename, std::ios_base::out | std::ios_base::binary | std::ios_base::trunc);
    2659           1 :             CATCH_REQUIRE(config_file.good());
    2660             :             config_file <<
    2661             :                 "# This comment is kept along the a= variable\n"
    2662             :                 "a color\n"
    2663             :                 "b red\n"
    2664           1 :                 "call-flag 122\n"
    2665             :             ;
    2666           1 :         }
    2667             : 
    2668           1 :         advgetopt::comment_t const comment(advgetopt::COMMENT_SHELL | advgetopt::COMMENT_SAVE);
    2669           1 :         advgetopt::conf_file_setup setup(SNAP_CATCH2_NAMESPACE::g_config_filename
    2670             :                             , advgetopt::line_continuation_t::line_continuation_single_line
    2671             :                             , advgetopt::ASSIGNMENT_OPERATOR_SPACE
    2672             :                             , comment
    2673             :                             , advgetopt::SECTION_OPERATOR_NONE
    2674           1 :                             , advgetopt::NAME_SEPARATOR_DASHES);
    2675             : 
    2676           1 :         CATCH_REQUIRE(setup.get_original_filename() == SNAP_CATCH2_NAMESPACE::g_config_filename);
    2677             : 
    2678           1 :         CATCH_REQUIRE(setup.is_valid());
    2679           1 :         CATCH_REQUIRE(setup.get_line_continuation() == advgetopt::line_continuation_t::line_continuation_single_line);
    2680           1 :         CATCH_REQUIRE(setup.get_assignment_operator() == advgetopt::ASSIGNMENT_OPERATOR_SPACE);
    2681           1 :         CATCH_REQUIRE(setup.get_comment() == comment);
    2682           1 :         CATCH_REQUIRE(setup.get_section_operator() == advgetopt::SECTION_OPERATOR_NONE);
    2683             : 
    2684           1 :         advgetopt::conf_file::pointer_t file(advgetopt::conf_file::get_conf_file(setup));
    2685             : 
    2686           1 :         CATCH_REQUIRE(file->exists());
    2687           1 :         CATCH_REQUIRE(file->get_setup().get_config_url() == setup.get_config_url());
    2688           1 :         CATCH_REQUIRE(file->get_errno() == 0);
    2689             : 
    2690           1 :         advgetopt::conf_file::sections_t sections(file->get_sections());
    2691           1 :         CATCH_REQUIRE(sections.empty());
    2692             : 
    2693           1 :         CATCH_REQUIRE(file->get_parameters().size() == 3);
    2694             : 
    2695           1 :         CATCH_REQUIRE(file->has_parameter("a"));
    2696           1 :         CATCH_REQUIRE(file->has_parameter("b"));
    2697           1 :         CATCH_REQUIRE(file->has_parameter("call-flag"));
    2698             : 
    2699           1 :         CATCH_REQUIRE(file->get_parameter("a") == "color");
    2700           1 :         CATCH_REQUIRE(file->get_parameter("b") == "red");
    2701           1 :         CATCH_REQUIRE(file->get_parameter("call-flag") == "122");
    2702             : 
    2703           1 :         advgetopt::conf_file::parameters_t params(file->get_parameters());
    2704           1 :         auto it(params.find("a"));
    2705           1 :         CATCH_REQUIRE(it != params.end());
    2706           1 :         CATCH_REQUIRE(it->second.get_comment() == "# This comment is kept along the a= variable\n");
    2707           1 :         CATCH_REQUIRE(it->second.get_comment(true) == "# This comment is kept along the a= variable\n");
    2708             : 
    2709           1 :         it->second.set_comment("# Changing the comment");
    2710           1 :         CATCH_REQUIRE(it->second.get_comment() == "# Changing the comment");
    2711           1 :         CATCH_REQUIRE(it->second.get_comment(true) == "# Changing the comment\n");
    2712             : 
    2713           1 :         CATCH_REQUIRE(file->save_configuration());
    2714             : 
    2715             :         // no backup since there was no modification so the save did nothing
    2716             :         //
    2717           1 :         CATCH_REQUIRE(access((SNAP_CATCH2_NAMESPACE::g_config_filename + ".bak").c_str(), F_OK) != 0);
    2718             : 
    2719           1 :         file->set_parameter(std::string(), "a", "size");
    2720           1 :         file->set_parameter(std::string(), "b", "tall");
    2721           1 :         file->set_parameter(std::string(), "call-flag", "1920");
    2722             : 
    2723           1 :         CATCH_REQUIRE(file->save_configuration());
    2724             : 
    2725           1 :         CATCH_REQUIRE(access((SNAP_CATCH2_NAMESPACE::g_config_filename + ".bak").c_str(), F_OK) == 0);
    2726             : 
    2727           1 :         file->set_parameter(std::string(), "a", "pace");
    2728           1 :         file->set_parameter(std::string(), "b", "fall");
    2729           1 :         file->set_parameter(std::string(), "call-flag", "2019");
    2730             : 
    2731           1 :         it->second.set_value("warning"); // WARNING: the parameters_t is a copy so you can't change the parameters that way
    2732           1 :         CATCH_REQUIRE(it->second.get_value() == "warning");
    2733           1 :         CATCH_REQUIRE(file->get_parameter("a") == "pace");
    2734             : 
    2735             :         // the following constructor and assignment are defined although not
    2736             :         // used within the library at the moment
    2737             :         //
    2738           3 :         advgetopt::parameter_value const value("other value");
    2739           1 :         it->second = value;
    2740           1 :         CATCH_REQUIRE(it->second.get_value() == "other value");
    2741           1 :         CATCH_REQUIRE(file->get_parameter("a") == "pace");
    2742             : 
    2743           1 :         CATCH_REQUIRE(file->save_configuration("save"));
    2744             : 
    2745           1 :         CATCH_REQUIRE(access((SNAP_CATCH2_NAMESPACE::g_config_filename + ".save").c_str(), F_OK) == 0);
    2746             : 
    2747           1 :         std::string const new_name(SNAP_CATCH2_NAMESPACE::g_config_filename + ".conf2");
    2748           1 :         rename(SNAP_CATCH2_NAMESPACE::g_config_filename.c_str(), new_name.c_str());
    2749             : 
    2750           1 :         advgetopt::conf_file_setup setup2(new_name
    2751             :                             , advgetopt::line_continuation_t::line_continuation_single_line
    2752             :                             , advgetopt::ASSIGNMENT_OPERATOR_SPACE
    2753             :                             , comment
    2754             :                             , advgetopt::SECTION_OPERATOR_NONE
    2755           1 :                             , advgetopt::NAME_SEPARATOR_DASHES);
    2756             : 
    2757           1 :         CATCH_REQUIRE(setup.get_original_filename() == SNAP_CATCH2_NAMESPACE::g_config_filename);
    2758             : 
    2759           1 :         CATCH_REQUIRE(setup2.is_valid());
    2760           1 :         CATCH_REQUIRE(setup2.get_line_continuation() == advgetopt::line_continuation_t::line_continuation_single_line);
    2761           1 :         CATCH_REQUIRE(setup2.get_assignment_operator() == advgetopt::ASSIGNMENT_OPERATOR_SPACE);
    2762           1 :         CATCH_REQUIRE(setup2.get_comment() == comment);
    2763           1 :         CATCH_REQUIRE(setup2.get_section_operator() == advgetopt::SECTION_OPERATOR_NONE);
    2764             : 
    2765           1 :         advgetopt::conf_file::pointer_t file2(advgetopt::conf_file::get_conf_file(setup2));
    2766             : 
    2767           1 :         CATCH_REQUIRE(file2->exists());
    2768           1 :         CATCH_REQUIRE(file2->get_setup().get_config_url() == setup2.get_config_url());
    2769           1 :         CATCH_REQUIRE(file2->get_errno() == 0);
    2770             : 
    2771           1 :         CATCH_REQUIRE(file->get_sections().empty());
    2772             : 
    2773           1 :         CATCH_REQUIRE(file2->get_parameters().size() == 3);
    2774             : 
    2775           1 :         CATCH_REQUIRE(file2->has_parameter("a"));
    2776           1 :         CATCH_REQUIRE(file2->has_parameter("b"));
    2777           1 :         CATCH_REQUIRE(file2->has_parameter("call-flag"));
    2778             : 
    2779           1 :         CATCH_REQUIRE(file2->get_parameter("a") == "pace");
    2780           1 :         CATCH_REQUIRE(file2->get_parameter("b") == "fall");
    2781           1 :         CATCH_REQUIRE(file2->get_parameter("call-flag") == "2019");
    2782             : 
    2783           1 :         file2->erase_all_parameters();
    2784           1 :         CATCH_REQUIRE_FALSE(file2->has_parameter("a"));
    2785           1 :         CATCH_REQUIRE_FALSE(file2->has_parameter("b"));
    2786           1 :         CATCH_REQUIRE_FALSE(file2->has_parameter("call-flag"));
    2787           1 :     }
    2788           3 :     CATCH_END_SECTION()
    2789           3 : }
    2790             : 
    2791             : 
    2792             : 
    2793             : 
    2794             : 
    2795             : 
    2796             : 
    2797           2 : CATCH_TEST_CASE("invalid_configuration_setup", "[config][getopt][invalid]")
    2798             : {
    2799           2 :     CATCH_START_SECTION("invalid_configuration_setup: empty filename")
    2800             :     {
    2801           3 :         CATCH_REQUIRE_THROWS_MATCHES(
    2802             :               advgetopt::conf_file_setup(
    2803             :                               std::string()
    2804             :                             , static_cast<advgetopt::line_continuation_t>(rand())
    2805             :                             , rand()
    2806             :                             , rand()
    2807             :                             , rand())
    2808             :             , advgetopt::getopt_invalid
    2809             :             , Catch::Matchers::ExceptionMessage(
    2810             :                           "getopt_exception: trying to load a configuration file using an empty filename."));
    2811             :     }
    2812           2 :     CATCH_END_SECTION()
    2813             : 
    2814           2 :     CATCH_START_SECTION("invalid_configuration_setup: invalid line continuation")
    2815             :     {
    2816           6 :         for(int count(0); count < 5; ++count)
    2817             :         {
    2818           5 :             advgetopt::line_continuation_t lc(advgetopt::line_continuation_t::line_continuation_unix);
    2819             :             do
    2820             :             {
    2821           5 :                 lc = static_cast<advgetopt::line_continuation_t>(rand());
    2822             :             }
    2823             :             while(lc >= advgetopt::line_continuation_t::line_continuation_single_line
    2824           5 :                && lc <= advgetopt::line_continuation_t::line_continuation_semicolon);
    2825             : 
    2826           5 :             advgetopt::conf_file_setup setup(
    2827             :                           "/etc/advgetopt/system.conf"
    2828             :                         , lc        // <- this is invalid
    2829          10 :                         , rand() & advgetopt::ASSIGNMENT_OPERATOR_MASK
    2830          10 :                         , rand() & advgetopt::COMMENT_MASK
    2831          15 :                         , rand() & advgetopt::SECTION_OPERATOR_MASK);
    2832             : 
    2833           5 :             CATCH_REQUIRE(setup.is_valid());
    2834             : 
    2835           5 :             CATCH_REQUIRE_THROWS_MATCHES(
    2836             :                   setup.get_config_url()
    2837             :                 , advgetopt::getopt_logic_error
    2838             :                 , Catch::Matchers::ExceptionMessage(
    2839             :                               "getopt_logic_error: unexpected line continuation."));
    2840           5 :         }
    2841             :     }
    2842           2 :     CATCH_END_SECTION()
    2843           2 : }
    2844             : 
    2845             : 
    2846             : 
    2847             : 
    2848             : 
    2849             : 
    2850             : 
    2851           1 : CATCH_TEST_CASE("config_reload_invalid_setup", "[config][getopt][invalid]")
    2852             : {
    2853           1 :     CATCH_START_SECTION("config_reload_invalid_setup: load a file, update it, verify it does not get reloaded")
    2854             :     {
    2855           1 :         SNAP_CATCH2_NAMESPACE::init_tmp_dir("invalid-reload", "load-twice-wrong-parameters");
    2856             : 
    2857             :         {
    2858           1 :             std::ofstream config_file;
    2859           1 :             config_file.open(SNAP_CATCH2_NAMESPACE::g_config_filename, std::ios_base::out | std::ios_base::binary | std::ios_base::trunc);
    2860           1 :             CATCH_REQUIRE(config_file.good());
    2861             :             config_file <<
    2862             :                 "# Auto-generated\n"
    2863             :                 "duplicates=work\n"
    2864             :                 "varying=parameters\n"
    2865             :                 "however=is\n"
    2866           1 :                 "not=valid\n"
    2867             :             ;
    2868           1 :         }
    2869             : 
    2870           1 :         advgetopt::conf_file_setup setup(SNAP_CATCH2_NAMESPACE::g_config_filename
    2871             :                             , advgetopt::line_continuation_t::line_continuation_single_line
    2872             :                             , advgetopt::ASSIGNMENT_OPERATOR_EQUAL
    2873             :                             , advgetopt::COMMENT_SHELL
    2874           1 :                             , advgetopt::SECTION_OPERATOR_NONE);
    2875             : 
    2876           1 :         CATCH_REQUIRE(setup.is_valid());
    2877           1 :         CATCH_REQUIRE(setup.get_line_continuation() == advgetopt::line_continuation_t::line_continuation_single_line);
    2878           1 :         CATCH_REQUIRE(setup.get_assignment_operator() == advgetopt::ASSIGNMENT_OPERATOR_EQUAL);
    2879           1 :         CATCH_REQUIRE(setup.get_comment() == advgetopt::COMMENT_SHELL);
    2880           1 :         CATCH_REQUIRE(setup.get_section_operator() == advgetopt::SECTION_OPERATOR_NONE);
    2881             : 
    2882           1 :         advgetopt::conf_file::pointer_t file(advgetopt::conf_file::get_conf_file(setup));
    2883             : 
    2884           1 :         CATCH_REQUIRE(file->get_setup().get_config_url() == setup.get_config_url());
    2885           1 :         CATCH_REQUIRE(file->get_errno() == 0);
    2886           1 :         CATCH_REQUIRE(file->get_sections().empty());
    2887           1 :         CATCH_REQUIRE(file->get_parameters().size() == 4);
    2888             : 
    2889           1 :         CATCH_REQUIRE(file->has_parameter("duplicates"));
    2890           1 :         CATCH_REQUIRE(file->has_parameter("varying"));
    2891           1 :         CATCH_REQUIRE(file->has_parameter("however"));
    2892           1 :         CATCH_REQUIRE(file->has_parameter("not"));
    2893             : 
    2894           1 :         CATCH_REQUIRE(file->get_parameter("duplicates") == "work");
    2895           1 :         CATCH_REQUIRE(file->get_parameter("varying") == "parameters");
    2896           1 :         CATCH_REQUIRE(file->get_parameter("however") == "is");
    2897           1 :         CATCH_REQUIRE(file->get_parameter("not") == "valid");
    2898             : 
    2899             :         // "reloading" that very same file but with the "wrong" parameters
    2900             :         // fails
    2901             :         //
    2902           7 :         for(int lc(static_cast<int>(advgetopt::line_continuation_t::line_continuation_single_line));
    2903           7 :             lc <= static_cast<int>(advgetopt::line_continuation_t::line_continuation_semicolon);
    2904             :             ++lc)
    2905             :         {
    2906           6 :             if(static_cast<advgetopt::line_continuation_t>(lc) == advgetopt::line_continuation_t::line_continuation_single_line)
    2907             :             {
    2908           1 :                 continue;
    2909             :             }
    2910             : 
    2911          85 :             for(advgetopt::assignment_operator_t ao(0);
    2912          85 :                 ao <= advgetopt::ASSIGNMENT_OPERATOR_MASK;
    2913             :                 ++ao)
    2914             :             {
    2915          80 :                 if(ao == advgetopt::ASSIGNMENT_OPERATOR_EQUAL)
    2916             :                 {
    2917           5 :                     continue;
    2918             :                 }
    2919             : 
    2920         600 :                 for(advgetopt::comment_t c(0);
    2921         600 :                     c < advgetopt::COMMENT_MASK;
    2922             :                     ++c)
    2923             :                 {
    2924         525 :                     if(c == advgetopt::COMMENT_SHELL)
    2925             :                     {
    2926          75 :                         continue;
    2927             :                     }
    2928             : 
    2929        7200 :                     for(advgetopt::section_operator_t so(0);
    2930        7200 :                         so < advgetopt::SECTION_OPERATOR_MASK;
    2931             :                         ++so)
    2932             :                     {
    2933        6750 :                         if(c == advgetopt::SECTION_OPERATOR_NONE)
    2934             :                         {
    2935        1125 :                             continue;
    2936             :                         }
    2937             : 
    2938        5625 :                         advgetopt::conf_file_setup different_setup(SNAP_CATCH2_NAMESPACE::g_config_filename
    2939             :                                         , static_cast<advgetopt::line_continuation_t>(lc)
    2940             :                                         , ao
    2941             :                                         , c
    2942        5625 :                                         , so);
    2943             : 
    2944        5625 :                         CATCH_REQUIRE_THROWS_MATCHES(
    2945             :                               advgetopt::conf_file::get_conf_file(different_setup)
    2946             :                             , advgetopt::getopt_logic_error
    2947             :                             , Catch::Matchers::ExceptionMessage(
    2948             :                                           "getopt_logic_error: trying to load configuration file \""
    2949             :                                         + different_setup.get_config_url()
    2950             :                                         + "\" but an existing configuration file with the same name was loaded with URL: \""
    2951             :                                         + setup.get_config_url()
    2952             :                                         + "\"."));
    2953        5625 :                     }
    2954             :                 }
    2955             :             }
    2956             :         }
    2957           1 :     }
    2958           1 :     CATCH_END_SECTION()
    2959           1 : }
    2960             : 
    2961             : 
    2962           1 : CATCH_TEST_CASE("missing_configuration_file", "[config][getopt][invalid]")
    2963             : {
    2964           1 :     CATCH_START_SECTION("missing_configuration_file: create a conf_file without the file")
    2965             :     {
    2966           6 :         for(int count(0); count < 5; ++count)
    2967             :         {
    2968           5 :             int const id(rand());
    2969          10 :             std::string const name("delete-file-" + std::to_string(id));
    2970             : 
    2971           5 :             SNAP_CATCH2_NAMESPACE::init_tmp_dir("delete", name);
    2972             : 
    2973             :             {
    2974           5 :                 std::ofstream config_file;
    2975           5 :                 config_file.open(SNAP_CATCH2_NAMESPACE::g_config_filename, std::ios_base::out | std::ios_base::binary | std::ios_base::trunc);
    2976           5 :                 CATCH_REQUIRE(config_file.good());
    2977             :                 config_file <<
    2978             :                     "# Auto-generated\n"
    2979           5 :                     "param=optional\n"
    2980             :                 ;
    2981           5 :             }
    2982             : 
    2983             :             // create the setup while the file still exists
    2984             :             //
    2985           5 :             advgetopt::conf_file_setup setup(
    2986             :                           SNAP_CATCH2_NAMESPACE::g_config_filename
    2987             :                         , advgetopt::line_continuation_t::line_continuation_unix
    2988             :                         , advgetopt::ASSIGNMENT_OPERATOR_EQUAL
    2989             :                         , advgetopt::COMMENT_SHELL
    2990           5 :                         , advgetopt::SECTION_OPERATOR_NONE);
    2991             : 
    2992             :             // get the full name before the unlink()
    2993           5 :             std::unique_ptr<char, decltype(&::free)> fn(realpath(SNAP_CATCH2_NAMESPACE::g_config_filename.c_str(), nullptr), &::free);
    2994             : 
    2995             :             // now unlink() that file
    2996             :             //
    2997           5 :             unlink(SNAP_CATCH2_NAMESPACE::g_config_filename.c_str());
    2998             : 
    2999             :             // still valid since we do not check again after the
    3000             :             // constructor ran
    3001             :             //
    3002           5 :             CATCH_REQUIRE(setup.is_valid());
    3003           5 :             CATCH_REQUIRE(setup.get_filename() == fn.get());
    3004           5 :             CATCH_REQUIRE(setup.get_line_continuation() == advgetopt::line_continuation_t::line_continuation_unix);
    3005           5 :             CATCH_REQUIRE(setup.get_assignment_operator() == advgetopt::ASSIGNMENT_OPERATOR_EQUAL);
    3006           5 :             CATCH_REQUIRE(setup.get_comment() == advgetopt::COMMENT_SHELL);
    3007           5 :             CATCH_REQUIRE(setup.get_section_operator() == advgetopt::SECTION_OPERATOR_NONE);
    3008             : 
    3009             :             // so when trying to create the conf_file object it fails
    3010             :             // opening the file
    3011             :             //
    3012           5 :             advgetopt::conf_file::pointer_t file(advgetopt::conf_file::get_conf_file(setup));
    3013           5 :             CATCH_REQUIRE(file->get_errno() == ENOENT);
    3014           5 :             CATCH_REQUIRE_FALSE(file->exists());
    3015           5 :         }
    3016             :     }
    3017           1 :     CATCH_END_SECTION()
    3018           1 : }
    3019             : 
    3020             : 
    3021           9 : CATCH_TEST_CASE("invalid_sections", "[config][getopt][invalid]")
    3022             : {
    3023           9 :     CATCH_START_SECTION("invalid_sections: variable name cannot start with a period when C operator is active")
    3024             :     {
    3025           1 :         SNAP_CATCH2_NAMESPACE::init_tmp_dir("invalid-section-operator", "period-name");
    3026             : 
    3027             :         {
    3028           1 :             std::ofstream config_file;
    3029           1 :             config_file.open(SNAP_CATCH2_NAMESPACE::g_config_filename, std::ios_base::out | std::ios_base::binary | std::ios_base::trunc);
    3030           1 :             CATCH_REQUIRE(config_file.good());
    3031             :             config_file <<
    3032             :                 "# Auto-generated\n"
    3033             :                 "a=color\n"
    3034             :                 "a..b=red\n"
    3035             :                 ".a.b.c=122\n"
    3036             :                 "m=size\n"
    3037             :                 "z=edge\n"
    3038             :                 "z.b=line\n"
    3039           1 :                 "z..b.c=12.72\n"
    3040             :             ;
    3041           1 :         }
    3042             : 
    3043           1 :         advgetopt::conf_file_setup setup(SNAP_CATCH2_NAMESPACE::g_config_filename
    3044             :                             , advgetopt::line_continuation_t::line_continuation_unix
    3045             :                             , advgetopt::ASSIGNMENT_OPERATOR_EQUAL
    3046             :                             , advgetopt::COMMENT_SHELL
    3047           1 :                             , advgetopt::SECTION_OPERATOR_C);
    3048             : 
    3049           1 :         CATCH_REQUIRE(setup.is_valid());
    3050           1 :         CATCH_REQUIRE(setup.get_line_continuation() == advgetopt::line_continuation_t::line_continuation_unix);
    3051           1 :         CATCH_REQUIRE(setup.get_assignment_operator() == advgetopt::ASSIGNMENT_OPERATOR_EQUAL);
    3052           1 :         CATCH_REQUIRE(setup.get_comment() == advgetopt::COMMENT_SHELL);
    3053           1 :         CATCH_REQUIRE(setup.get_section_operator() == advgetopt::SECTION_OPERATOR_C);
    3054             : 
    3055           1 :         SNAP_CATCH2_NAMESPACE::push_expected_log(
    3056             :                     "error: option name \".a.b.c\" cannot start with"
    3057             :                     " a period (.).");
    3058           1 :         advgetopt::conf_file::pointer_t file(advgetopt::conf_file::get_conf_file(setup));
    3059           1 :         SNAP_CATCH2_NAMESPACE::expected_logs_stack_is_empty();
    3060             : 
    3061           1 :         CATCH_REQUIRE(file->get_setup().get_config_url() == setup.get_config_url());
    3062           1 :         CATCH_REQUIRE(file->get_errno() == 0);
    3063             : 
    3064           1 :         advgetopt::conf_file::sections_t sections(file->get_sections());
    3065           1 :         CATCH_REQUIRE(sections.size() == 3);
    3066           1 :         CATCH_REQUIRE(sections.find("a")    != sections.end());
    3067           1 :         CATCH_REQUIRE(sections.find("z")    != sections.end());
    3068           1 :         CATCH_REQUIRE(sections.find("z::b") != sections.end());
    3069             : 
    3070           1 :         CATCH_REQUIRE(file->get_parameters().size() == 6);
    3071             : 
    3072           1 :         CATCH_REQUIRE(file->has_parameter("a"));
    3073           1 :         CATCH_REQUIRE(file->has_parameter("a::b"));
    3074           1 :         CATCH_REQUIRE(file->has_parameter("m"));
    3075           1 :         CATCH_REQUIRE(file->has_parameter("z"));
    3076           1 :         CATCH_REQUIRE(file->has_parameter("z::b"));
    3077           1 :         CATCH_REQUIRE(file->has_parameter("z::b::c"));
    3078             : 
    3079           1 :         CATCH_REQUIRE(file->get_parameter("a") == "color");
    3080           1 :         CATCH_REQUIRE(file->get_parameter("a::b") == "red");
    3081           1 :         CATCH_REQUIRE(file->get_parameter("m") == "size");
    3082           1 :         CATCH_REQUIRE(file->get_parameter("z") == "edge");
    3083           1 :         CATCH_REQUIRE(file->get_parameter("z::b") == "line");
    3084           1 :         CATCH_REQUIRE(file->get_parameter("z::b::c") == "12.72");
    3085           1 :     }
    3086           9 :     CATCH_END_SECTION()
    3087             : 
    3088           9 :     CATCH_START_SECTION("invalid_sections: two section operators one after another can cause trouble")
    3089             :     {
    3090           1 :         SNAP_CATCH2_NAMESPACE::init_tmp_dir("invalid-section-operator", "name-period-cpp-name");
    3091             : 
    3092             :         {
    3093           1 :             std::ofstream config_file;
    3094           1 :             config_file.open(SNAP_CATCH2_NAMESPACE::g_config_filename, std::ios_base::out | std::ios_base::binary | std::ios_base::trunc);
    3095           1 :             CATCH_REQUIRE(config_file.good());
    3096             :             config_file <<
    3097             :                 "# Auto-generated\n"
    3098             :                 "a=color\n"
    3099             :                 "a..b=red\n"
    3100             :                 "a.::b.c=122\n"
    3101             :                 "m=size\n"
    3102             :                 "z=edge\n"
    3103             :                 "z.b=line\n"
    3104           1 :                 "z..b.c=12.72\n"
    3105             :             ;
    3106           1 :         }
    3107             : 
    3108           1 :         advgetopt::conf_file_setup setup(SNAP_CATCH2_NAMESPACE::g_config_filename
    3109             :                             , advgetopt::line_continuation_t::line_continuation_unix
    3110             :                             , advgetopt::ASSIGNMENT_OPERATOR_EQUAL
    3111             :                             , advgetopt::COMMENT_SHELL
    3112           1 :                             , advgetopt::SECTION_OPERATOR_C | advgetopt::SECTION_OPERATOR_CPP);
    3113             : 
    3114           1 :         CATCH_REQUIRE(setup.is_valid());
    3115           1 :         CATCH_REQUIRE(setup.get_line_continuation() == advgetopt::line_continuation_t::line_continuation_unix);
    3116           1 :         CATCH_REQUIRE(setup.get_assignment_operator() == advgetopt::ASSIGNMENT_OPERATOR_EQUAL);
    3117           1 :         CATCH_REQUIRE(setup.get_comment() == advgetopt::COMMENT_SHELL);
    3118           1 :         CATCH_REQUIRE(setup.get_section_operator() == (advgetopt::SECTION_OPERATOR_C | advgetopt::SECTION_OPERATOR_CPP));
    3119             : 
    3120           1 :         SNAP_CATCH2_NAMESPACE::push_expected_log(
    3121             :                     "error: option name \"a.::b.c\" cannot start with"
    3122             :                     " a scope operator (::).");
    3123           1 :         advgetopt::conf_file::pointer_t file(advgetopt::conf_file::get_conf_file(setup));
    3124           1 :         SNAP_CATCH2_NAMESPACE::expected_logs_stack_is_empty();
    3125             : 
    3126           1 :         CATCH_REQUIRE(file->get_setup().get_config_url() == setup.get_config_url());
    3127           1 :         CATCH_REQUIRE(file->get_errno() == 0);
    3128             : 
    3129           1 :         advgetopt::conf_file::sections_t sections(file->get_sections());
    3130           1 :         CATCH_REQUIRE(sections.size() == 3);
    3131           1 :         CATCH_REQUIRE(sections.find("a")    != sections.end());
    3132           1 :         CATCH_REQUIRE(sections.find("z")    != sections.end());
    3133           1 :         CATCH_REQUIRE(sections.find("z::b") != sections.end());
    3134             : 
    3135           1 :         CATCH_REQUIRE(file->get_parameters().size() == 6);
    3136             : 
    3137           1 :         CATCH_REQUIRE(file->has_parameter("a"));
    3138           1 :         CATCH_REQUIRE(file->has_parameter("a::b"));
    3139           1 :         CATCH_REQUIRE(file->has_parameter("m"));
    3140           1 :         CATCH_REQUIRE(file->has_parameter("z"));
    3141           1 :         CATCH_REQUIRE(file->has_parameter("z::b"));
    3142           1 :         CATCH_REQUIRE(file->has_parameter("z::b::c"));
    3143             : 
    3144           1 :         CATCH_REQUIRE(file->get_parameter("a") == "color");
    3145           1 :         CATCH_REQUIRE(file->get_parameter("a::b") == "red");
    3146           1 :         CATCH_REQUIRE(file->get_parameter("m") == "size");
    3147           1 :         CATCH_REQUIRE(file->get_parameter("z") == "edge");
    3148           1 :         CATCH_REQUIRE(file->get_parameter("z::b") == "line");
    3149           1 :         CATCH_REQUIRE(file->get_parameter("z::b::c") == "12.72");
    3150           1 :     }
    3151           9 :     CATCH_END_SECTION()
    3152             : 
    3153           9 :     CATCH_START_SECTION("invalid_sections: section operator cannot appear at the end")
    3154             :     {
    3155           1 :         SNAP_CATCH2_NAMESPACE::init_tmp_dir("invalid-section-operator", "name-period-name-cpp");
    3156             : 
    3157             :         {
    3158           1 :             std::ofstream config_file;
    3159           1 :             config_file.open(SNAP_CATCH2_NAMESPACE::g_config_filename, std::ios_base::out | std::ios_base::binary | std::ios_base::trunc);
    3160           1 :             CATCH_REQUIRE(config_file.good());
    3161             :             config_file <<
    3162             :                 "# Auto-generated\n"
    3163             :                 "a=color\n"
    3164             :                 "a..b=red\n"
    3165             :                 "a.b.c::=122\n"
    3166             :                 "m=size\n"
    3167             :                 "z=edge\n"
    3168             :                 "z.b=line\n"
    3169           1 :                 "z..b.c=12.72\n"
    3170             :             ;
    3171           1 :         }
    3172             : 
    3173           1 :         advgetopt::conf_file_setup setup(SNAP_CATCH2_NAMESPACE::g_config_filename
    3174             :                             , advgetopt::line_continuation_t::line_continuation_unix
    3175             :                             , advgetopt::ASSIGNMENT_OPERATOR_EQUAL
    3176             :                             , advgetopt::COMMENT_SHELL
    3177           1 :                             , advgetopt::SECTION_OPERATOR_C | advgetopt::SECTION_OPERATOR_CPP);
    3178             : 
    3179           1 :         CATCH_REQUIRE(setup.is_valid());
    3180           1 :         CATCH_REQUIRE(setup.get_line_continuation() == advgetopt::line_continuation_t::line_continuation_unix);
    3181           1 :         CATCH_REQUIRE(setup.get_assignment_operator() == advgetopt::ASSIGNMENT_OPERATOR_EQUAL);
    3182           1 :         CATCH_REQUIRE(setup.get_comment() == advgetopt::COMMENT_SHELL);
    3183           1 :         CATCH_REQUIRE(setup.get_section_operator() == (advgetopt::SECTION_OPERATOR_C | advgetopt::SECTION_OPERATOR_CPP));
    3184             : 
    3185           1 :         SNAP_CATCH2_NAMESPACE::push_expected_log(
    3186             :                     "error: option name \"a.b.c::\" cannot end with a"
    3187             :                     " section operator or be empty.");
    3188           1 :         advgetopt::conf_file::pointer_t file(advgetopt::conf_file::get_conf_file(setup));
    3189           1 :         SNAP_CATCH2_NAMESPACE::expected_logs_stack_is_empty();
    3190             : 
    3191           1 :         CATCH_REQUIRE(file->get_setup().get_config_url() == setup.get_config_url());
    3192           1 :         CATCH_REQUIRE(file->get_errno() == 0);
    3193             : 
    3194           1 :         advgetopt::conf_file::sections_t sections(file->get_sections());
    3195           1 :         CATCH_REQUIRE(sections.size() == 3);
    3196           1 :         CATCH_REQUIRE(sections.find("a")    != sections.end());
    3197           1 :         CATCH_REQUIRE(sections.find("z")    != sections.end());
    3198           1 :         CATCH_REQUIRE(sections.find("z::b") != sections.end());
    3199             : 
    3200           1 :         CATCH_REQUIRE(file->get_parameters().size() == 6);
    3201             : 
    3202           1 :         CATCH_REQUIRE(file->has_parameter("a"));
    3203           1 :         CATCH_REQUIRE(file->has_parameter("a::b"));
    3204           1 :         CATCH_REQUIRE(file->has_parameter("m"));
    3205           1 :         CATCH_REQUIRE(file->has_parameter("z"));
    3206           1 :         CATCH_REQUIRE(file->has_parameter("z::b"));
    3207           1 :         CATCH_REQUIRE(file->has_parameter("z::b::c"));
    3208             : 
    3209           1 :         CATCH_REQUIRE(file->get_parameter("a") == "color");
    3210           1 :         CATCH_REQUIRE(file->get_parameter("a::b") == "red");
    3211           1 :         CATCH_REQUIRE(file->get_parameter("m") == "size");
    3212           1 :         CATCH_REQUIRE(file->get_parameter("z") == "edge");
    3213           1 :         CATCH_REQUIRE(file->get_parameter("z::b") == "line");
    3214           1 :         CATCH_REQUIRE(file->get_parameter("z::b::c") == "12.72");
    3215           1 :     }
    3216           9 :     CATCH_END_SECTION()
    3217             : 
    3218           9 :     CATCH_START_SECTION("invalid_sections: sections not allowed")
    3219             :     {
    3220           1 :         SNAP_CATCH2_NAMESPACE::init_tmp_dir("invalid-section-operator", "section-not-allowed");
    3221             : 
    3222             :         {
    3223           1 :             std::ofstream config_file;
    3224           1 :             config_file.open(SNAP_CATCH2_NAMESPACE::g_config_filename, std::ios_base::out | std::ios_base::binary | std::ios_base::trunc);
    3225           1 :             CATCH_REQUIRE(config_file.good());
    3226             :             config_file <<
    3227             :                 "# Auto-generated\n"
    3228             :                 "a=color\n"
    3229             :                 "a::b=red\n"
    3230             :                 "m.n=size\n"
    3231           1 :                 "z=edge\n"
    3232             :             ;
    3233           1 :         }
    3234             : 
    3235             :         // no errors here since we do not detect the sections in this case
    3236             :         //
    3237           1 :         advgetopt::conf_file_setup setup(SNAP_CATCH2_NAMESPACE::g_config_filename
    3238             :                             , advgetopt::line_continuation_t::line_continuation_unix
    3239             :                             , advgetopt::ASSIGNMENT_OPERATOR_EQUAL
    3240             :                             , advgetopt::COMMENT_SHELL
    3241           1 :                             , advgetopt::SECTION_OPERATOR_NONE);
    3242             : 
    3243           1 :         CATCH_REQUIRE(setup.is_valid());
    3244           1 :         CATCH_REQUIRE(setup.get_line_continuation() == advgetopt::line_continuation_t::line_continuation_unix);
    3245           1 :         CATCH_REQUIRE(setup.get_assignment_operator() == advgetopt::ASSIGNMENT_OPERATOR_EQUAL);
    3246           1 :         CATCH_REQUIRE(setup.get_comment() == advgetopt::COMMENT_SHELL);
    3247           1 :         CATCH_REQUIRE(setup.get_section_operator() == (advgetopt::SECTION_OPERATOR_NONE));
    3248             : 
    3249           1 :         std::unique_ptr<char, decltype(&::free)> fn(realpath(SNAP_CATCH2_NAMESPACE::g_config_filename.c_str(), nullptr), &::free);
    3250           2 :         SNAP_CATCH2_NAMESPACE::push_expected_log(
    3251             :                   "error: section \"a::b\" from parameter \"a::b\" on line 3 in configuration file \""
    3252           2 :                 + std::string(fn.get())
    3253           3 :                 + "\" includes a character (\\072) not acceptable for a section or"
    3254             :                   " parameter name (controls, space, quotes, and \";#/=:?+\\\").");
    3255           1 :         advgetopt::conf_file::pointer_t file(advgetopt::conf_file::get_conf_file(setup));
    3256           1 :         SNAP_CATCH2_NAMESPACE::expected_logs_stack_is_empty();
    3257             : 
    3258           1 :         CATCH_REQUIRE(file->get_setup().get_config_url() == setup.get_config_url());
    3259           1 :         CATCH_REQUIRE(file->get_errno() == 0);
    3260             : 
    3261           1 :         advgetopt::conf_file::sections_t sections(file->get_sections());
    3262           1 :         CATCH_REQUIRE(sections.empty());
    3263             : 
    3264           1 :         CATCH_REQUIRE(file->get_parameters().size() == 3);
    3265             : 
    3266           1 :         CATCH_REQUIRE(file->has_parameter("a"));
    3267           1 :         CATCH_REQUIRE_FALSE(file->has_parameter("a::b"));
    3268           1 :         CATCH_REQUIRE(file->has_parameter("m.n"));
    3269           1 :         CATCH_REQUIRE(file->has_parameter("z"));
    3270             : 
    3271           1 :         CATCH_REQUIRE(file->get_parameter("a") == "color");
    3272           1 :         CATCH_REQUIRE(file->get_parameter("a::b") == std::string());
    3273           1 :         CATCH_REQUIRE(file->get_parameter("m.n") == "size");
    3274           1 :         CATCH_REQUIRE(file->get_parameter("z") == "edge");
    3275             : 
    3276           1 :         SNAP_CATCH2_NAMESPACE::push_expected_log(
    3277             :                     "error: option name \"blue::shepard\" cannot be added to"
    3278             :                     " section \"j::k\" because there is no section support"
    3279             :                     " for this configuration file.");
    3280           1 :         file->set_parameter("j::k", "blue::shepard", "2001");
    3281           1 :         SNAP_CATCH2_NAMESPACE::expected_logs_stack_is_empty();
    3282           1 :     }
    3283           9 :     CATCH_END_SECTION()
    3284             : 
    3285           9 :     CATCH_START_SECTION("invalid_sections: invalid characters in names")
    3286             :     {
    3287           1 :         std::string const bad_chars(
    3288             :                     "\x01\x02\x03\x04\x05\x06\x07"
    3289             :                 "\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F"
    3290             :                 "\x10\x11\x12\x13\x14\x15\x16\x17"
    3291             :                 "\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F"
    3292             :                 "\x20"
    3293             :                 "'\";#/=:?+\\"
    3294           2 :             );
    3295          43 :         for(auto c : bad_chars)
    3296             :         {
    3297             :             // white spaces get removed from the line so we cannot test
    3298             :             // them in this way
    3299             :             //
    3300          42 :             if(std::iswspace(c))
    3301             :             {
    3302           6 :                 continue;
    3303             :             }
    3304          36 :             std::string bc;
    3305          36 :             bc += c;
    3306             : 
    3307         144 :             for(int pos(0); pos < 3; ++pos)
    3308             :             {
    3309         216 :                 std::string spos("undefined");
    3310         216 :                 std::string bad_char("undefined");
    3311         108 :                 switch(pos)
    3312             :                 {
    3313          36 :                 case 0:
    3314          36 :                     spos = "start";
    3315          36 :                     bad_char = bc + "bad-char";
    3316          36 :                     break;
    3317             : 
    3318          36 :                 case 1:
    3319          36 :                     spos = "middle";
    3320          36 :                     bad_char = "bad" + bc + "char";
    3321          36 :                     break;
    3322             : 
    3323          36 :                 case 2:
    3324          36 :                     spos = "end";
    3325          36 :                     bad_char = "bad-char" + bc;
    3326          36 :                     break;
    3327             : 
    3328             :                 }
    3329         108 :                 SNAP_CATCH2_NAMESPACE::init_tmp_dir("invalid-characters", "bad-character-" + std::to_string(static_cast<int>(c)) + "-" + spos);
    3330             : 
    3331             :                 {
    3332         108 :                     std::ofstream config_file;
    3333         108 :                     config_file.open(SNAP_CATCH2_NAMESPACE::g_config_filename, std::ios_base::out | std::ios_base::binary | std::ios_base::trunc);
    3334         108 :                     CATCH_REQUIRE(config_file.good());
    3335         108 :                     char op(c == '=' ? ':' : '=');
    3336             :                     config_file <<
    3337             :                            "good" << op << "red\n"
    3338             :                         << bad_char << op << "color\n"       // <-- bad char
    3339         108 :                            "fine" << op << "param\n";
    3340             :                     ;
    3341         108 :                 }
    3342             : 
    3343             :                 // no errors here since we do not detect the sections in this case
    3344             :                 //
    3345         108 :                 advgetopt::assignment_operator_t as(c == '='
    3346         108 :                                         ? advgetopt::ASSIGNMENT_OPERATOR_COLON
    3347             :                                         : advgetopt::ASSIGNMENT_OPERATOR_EQUAL);
    3348         108 :                 advgetopt::conf_file_setup setup(SNAP_CATCH2_NAMESPACE::g_config_filename
    3349             :                                     , advgetopt::line_continuation_t::line_continuation_unix
    3350             :                                     , as
    3351             :                                     , advgetopt::COMMENT_NONE
    3352         216 :                                     , advgetopt::SECTION_OPERATOR_NONE);
    3353             : 
    3354         108 :                 CATCH_REQUIRE(setup.is_valid());
    3355         108 :                 CATCH_REQUIRE(setup.get_line_continuation() == advgetopt::line_continuation_t::line_continuation_unix);
    3356         108 :                 CATCH_REQUIRE(setup.get_assignment_operator() == as);
    3357         108 :                 CATCH_REQUIRE(setup.get_comment() == advgetopt::COMMENT_NONE);
    3358         108 :                 CATCH_REQUIRE(setup.get_section_operator() == (advgetopt::SECTION_OPERATOR_NONE));
    3359             : 
    3360         216 :                 std::unique_ptr<char, decltype(&::free)> fn(realpath(SNAP_CATCH2_NAMESPACE::g_config_filename.c_str(), nullptr), &::free);
    3361         216 :                 std::stringstream octal_char;
    3362         108 :                 octal_char << std::oct << std::setw(3) << std::setfill('0') << static_cast<int>(c);
    3363         216 :                 SNAP_CATCH2_NAMESPACE::push_expected_log(
    3364             :                           "error: section \""
    3365         216 :                         + bad_char
    3366         324 :                         + "\" from parameter \""
    3367         324 :                         + bad_char
    3368         324 :                         + "\" on line 2 in configuration file \""
    3369         324 :                         + fn.get()
    3370         324 :                         + "\" includes a character (\\"
    3371         432 :                         + octal_char.str()
    3372         324 :                         + ") not acceptable for a section or"
    3373             :                           " parameter name (controls, space, quotes, and \";#/=:?+\\\").");
    3374         216 :                 advgetopt::conf_file::pointer_t file(advgetopt::conf_file::get_conf_file(setup));
    3375             : 
    3376         108 :                 CATCH_REQUIRE(file->get_setup().get_config_url() == setup.get_config_url());
    3377         108 :                 CATCH_REQUIRE(file->get_errno() == 0);
    3378             : 
    3379         216 :                 advgetopt::conf_file::sections_t sections(file->get_sections());
    3380         108 :                 CATCH_REQUIRE(sections.empty());
    3381             : 
    3382         108 :                 CATCH_REQUIRE(file->get_parameters().size() == 2);
    3383             : 
    3384         108 :                 CATCH_REQUIRE(file->has_parameter("good"));
    3385         108 :                 CATCH_REQUIRE_FALSE(file->has_parameter(bad_char));
    3386         108 :                 CATCH_REQUIRE(file->has_parameter("fine"));
    3387             : 
    3388         108 :                 CATCH_REQUIRE(file->get_parameter("good") == "red");
    3389         108 :                 CATCH_REQUIRE(file->get_parameter(bad_char) == std::string());
    3390         108 :                 CATCH_REQUIRE(file->get_parameter("fine") == "param");
    3391         108 :             }
    3392          36 :         }
    3393           1 :         SNAP_CATCH2_NAMESPACE::expected_logs_stack_is_empty();
    3394           1 :     }
    3395           9 :     CATCH_END_SECTION()
    3396             : 
    3397           9 :     CATCH_START_SECTION("invalid_sections: too many sections")
    3398             :     {
    3399           1 :         SNAP_CATCH2_NAMESPACE::init_tmp_dir("invalid-section-operator", "too-many-sections");
    3400             : 
    3401             :         {
    3402           1 :             std::ofstream config_file;
    3403           1 :             config_file.open(SNAP_CATCH2_NAMESPACE::g_config_filename, std::ios_base::out | std::ios_base::binary | std::ios_base::trunc);
    3404           1 :             CATCH_REQUIRE(config_file.good());
    3405             :             config_file <<
    3406             :                 "# Auto-generated\n"
    3407             :                 "a=color\n"
    3408             :                 "a::b=red\n"
    3409             :                 "m.n.o=size\n"
    3410           1 :                 "z=edge\n"
    3411             :             ;
    3412           1 :         }
    3413             : 
    3414           1 :         advgetopt::conf_file_setup setup(SNAP_CATCH2_NAMESPACE::g_config_filename
    3415             :                             , advgetopt::line_continuation_t::line_continuation_unix
    3416             :                             , advgetopt::ASSIGNMENT_OPERATOR_EQUAL
    3417             :                             , advgetopt::COMMENT_SHELL
    3418           1 :                             , advgetopt::SECTION_OPERATOR_CPP | advgetopt::SECTION_OPERATOR_C | advgetopt::SECTION_OPERATOR_ONE_SECTION);
    3419             : 
    3420           1 :         CATCH_REQUIRE(setup.is_valid());
    3421           1 :         CATCH_REQUIRE(setup.get_line_continuation() == advgetopt::line_continuation_t::line_continuation_unix);
    3422           1 :         CATCH_REQUIRE(setup.get_assignment_operator() == advgetopt::ASSIGNMENT_OPERATOR_EQUAL);
    3423           1 :         CATCH_REQUIRE(setup.get_comment() == advgetopt::COMMENT_SHELL);
    3424           1 :         CATCH_REQUIRE(setup.get_section_operator() == (advgetopt::SECTION_OPERATOR_CPP | advgetopt::SECTION_OPERATOR_C | advgetopt::SECTION_OPERATOR_ONE_SECTION));
    3425             : 
    3426           1 :         SNAP_CATCH2_NAMESPACE::push_expected_log(
    3427             :                     "error: option name \"m.n.o\" cannot be added to section"
    3428             :                     " \"m::n\" because this configuration only accepts one"
    3429             :                     " section level.");
    3430           1 :         advgetopt::conf_file::pointer_t file(advgetopt::conf_file::get_conf_file(setup));
    3431           1 :         SNAP_CATCH2_NAMESPACE::expected_logs_stack_is_empty();
    3432             : 
    3433           1 :         CATCH_REQUIRE(file->get_setup().get_config_url() == setup.get_config_url());
    3434           1 :         CATCH_REQUIRE(file->get_errno() == 0);
    3435             : 
    3436           1 :         advgetopt::conf_file::sections_t sections(file->get_sections());
    3437           1 :         CATCH_REQUIRE(sections.size() == 1);
    3438           1 :         CATCH_REQUIRE(sections.find("a") != sections.end());
    3439             : 
    3440           1 :         CATCH_REQUIRE(file->get_parameters().size() == 3);
    3441             : 
    3442           1 :         CATCH_REQUIRE(file->has_parameter("a"));
    3443           1 :         CATCH_REQUIRE(file->has_parameter("a::b"));
    3444           1 :         CATCH_REQUIRE(file->has_parameter("z"));
    3445             : 
    3446           1 :         CATCH_REQUIRE(file->get_parameter("a") == "color");
    3447           1 :         CATCH_REQUIRE(file->get_parameter("a::b") == "red");
    3448           1 :         CATCH_REQUIRE(file->get_parameter("z") == "edge");
    3449           1 :     }
    3450           9 :     CATCH_END_SECTION()
    3451             : 
    3452           9 :     CATCH_START_SECTION("invalid_sections: all '{' were not closed")
    3453             :     {
    3454           1 :         SNAP_CATCH2_NAMESPACE::init_tmp_dir("invalid-section-operator", "unclosed-brackets");
    3455             : 
    3456             :         {
    3457           1 :             std::ofstream config_file;
    3458           1 :             config_file.open(SNAP_CATCH2_NAMESPACE::g_config_filename, std::ios_base::out | std::ios_base::binary | std::ios_base::trunc);
    3459           1 :             CATCH_REQUIRE(config_file.good());
    3460             :             config_file <<
    3461             :                 "# Auto-generated\n"
    3462             :                 "colors {\n"
    3463             :                 "  b=red\n"
    3464           1 :                 "  c=blue\n"
    3465             :             ;
    3466           1 :         }
    3467             : 
    3468           1 :         advgetopt::conf_file_setup setup(SNAP_CATCH2_NAMESPACE::g_config_filename
    3469             :                             , advgetopt::line_continuation_t::line_continuation_unix
    3470             :                             , advgetopt::ASSIGNMENT_OPERATOR_EQUAL
    3471             :                             , advgetopt::COMMENT_SHELL
    3472           1 :                             , advgetopt::SECTION_OPERATOR_BLOCK);
    3473             : 
    3474           1 :         CATCH_REQUIRE(setup.is_valid());
    3475           1 :         CATCH_REQUIRE(setup.get_line_continuation() == advgetopt::line_continuation_t::line_continuation_unix);
    3476           1 :         CATCH_REQUIRE(setup.get_assignment_operator() == advgetopt::ASSIGNMENT_OPERATOR_EQUAL);
    3477           1 :         CATCH_REQUIRE(setup.get_comment() == advgetopt::COMMENT_SHELL);
    3478           1 :         CATCH_REQUIRE(setup.get_section_operator() == advgetopt::SECTION_OPERATOR_BLOCK);
    3479             : 
    3480           1 :         std::unique_ptr<char, decltype(&::free)> fn(realpath(SNAP_CATCH2_NAMESPACE::g_config_filename.c_str(), nullptr), &::free);
    3481           2 :         SNAP_CATCH2_NAMESPACE::push_expected_log(
    3482             :                     "error: unterminated `section { ... }`, the `}` is missing"
    3483             :                     " in configuration file "
    3484             :                     "\""
    3485           2 :                   + std::string(fn.get())
    3486           3 :                   + "\".");
    3487           1 :         advgetopt::conf_file::pointer_t file(advgetopt::conf_file::get_conf_file(setup));
    3488           1 :         SNAP_CATCH2_NAMESPACE::expected_logs_stack_is_empty();
    3489             : 
    3490           1 :         CATCH_REQUIRE(file->get_setup().get_config_url() == setup.get_config_url());
    3491           1 :         CATCH_REQUIRE(file->get_errno() == 0);
    3492             : 
    3493           1 :         advgetopt::conf_file::sections_t sections(file->get_sections());
    3494           1 :         CATCH_REQUIRE(sections.size() == 1);
    3495           1 :         CATCH_REQUIRE(sections.find("colors") != sections.end());
    3496             : 
    3497           1 :         CATCH_REQUIRE(file->get_parameters().size() == 2);
    3498             : 
    3499           1 :         CATCH_REQUIRE(file->has_parameter("colors::b"));
    3500           1 :         CATCH_REQUIRE(file->has_parameter("colors::c"));
    3501             : 
    3502           1 :         CATCH_REQUIRE(file->get_parameter("colors::b") == "red");
    3503           1 :         CATCH_REQUIRE(file->get_parameter("colors::c") == "blue");
    3504           1 :     }
    3505           9 :     CATCH_END_SECTION()
    3506             : 
    3507           9 :     CATCH_START_SECTION("invalid_sections: data after ']' in INI file")
    3508             :     {
    3509           1 :         SNAP_CATCH2_NAMESPACE::init_tmp_dir("invalid-section-operator", "additional-data");
    3510             : 
    3511             :         {
    3512           1 :             std::ofstream config_file;
    3513           1 :             config_file.open(SNAP_CATCH2_NAMESPACE::g_config_filename, std::ios_base::out | std::ios_base::binary | std::ios_base::trunc);
    3514           1 :             CATCH_REQUIRE(config_file.good());
    3515             :             config_file <<
    3516             :                 "# Auto-generated\n"
    3517             :                 "[colors]\n"
    3518             :                 "b=red\n"
    3519             :                 "c=blue\n"
    3520             :                 "\n"
    3521             :                 "[sizes] comment\n"     // <- missing the comment introducer
    3522             :                 "q=1000\n"
    3523           1 :                 "r=9999\n"
    3524             :             ;
    3525           1 :         }
    3526             : 
    3527           1 :         advgetopt::conf_file_setup setup(SNAP_CATCH2_NAMESPACE::g_config_filename
    3528             :                             , advgetopt::line_continuation_t::line_continuation_unix
    3529             :                             , advgetopt::ASSIGNMENT_OPERATOR_EQUAL
    3530             :                             , advgetopt::COMMENT_SHELL
    3531           1 :                             , advgetopt::SECTION_OPERATOR_INI_FILE);
    3532             : 
    3533           1 :         CATCH_REQUIRE(setup.is_valid());
    3534           1 :         CATCH_REQUIRE(setup.get_line_continuation() == advgetopt::line_continuation_t::line_continuation_unix);
    3535           1 :         CATCH_REQUIRE(setup.get_assignment_operator() == advgetopt::ASSIGNMENT_OPERATOR_EQUAL);
    3536           1 :         CATCH_REQUIRE(setup.get_comment() == advgetopt::COMMENT_SHELL);
    3537           1 :         CATCH_REQUIRE(setup.get_section_operator() == advgetopt::SECTION_OPERATOR_INI_FILE);
    3538             : 
    3539           1 :         std::unique_ptr<char, decltype(&::free)> fn(realpath(SNAP_CATCH2_NAMESPACE::g_config_filename.c_str(), nullptr), &::free);
    3540           2 :         SNAP_CATCH2_NAMESPACE::push_expected_log(
    3541             :                       "error: section names in configuration files cannot be followed by anything other than spaces in"
    3542             :                       " \"[sizes] comment\" on line 6 from configuration file \""
    3543           2 :                     + std::string(fn.get())
    3544           3 :                     + "\".");
    3545           1 :         advgetopt::conf_file::pointer_t file(advgetopt::conf_file::get_conf_file(setup));
    3546           1 :         SNAP_CATCH2_NAMESPACE::expected_logs_stack_is_empty();
    3547             : 
    3548           1 :         CATCH_REQUIRE(file->get_setup().get_config_url() == setup.get_config_url());
    3549           1 :         CATCH_REQUIRE(file->get_errno() == 0);
    3550             : 
    3551           1 :         advgetopt::conf_file::sections_t sections(file->get_sections());
    3552           1 :         CATCH_REQUIRE(sections.size() == 1);
    3553           1 :         CATCH_REQUIRE(sections.find("colors") != sections.end());
    3554             : 
    3555           1 :         CATCH_REQUIRE(file->get_parameters().size() == 4);
    3556             : 
    3557           1 :         CATCH_REQUIRE(file->has_parameter("colors::b"));
    3558           1 :         CATCH_REQUIRE(file->has_parameter("colors::c"));
    3559           1 :         CATCH_REQUIRE(file->has_parameter("colors::q"));
    3560           1 :         CATCH_REQUIRE(file->has_parameter("colors::r"));
    3561             : 
    3562           1 :         CATCH_REQUIRE(file->get_parameter("colors::b") == "red");
    3563           1 :         CATCH_REQUIRE(file->get_parameter("colors::c") == "blue");
    3564           1 :         CATCH_REQUIRE(file->get_parameter("colors::q") == "1000");
    3565           1 :         CATCH_REQUIRE(file->get_parameter("colors::r") == "9999");
    3566           1 :     }
    3567           9 :     CATCH_END_SECTION()
    3568             : 
    3569           9 :     CATCH_START_SECTION("invalid_sections: INI file section inside a block is not allowed")
    3570             :     {
    3571           1 :         SNAP_CATCH2_NAMESPACE::init_tmp_dir("invalid-section-operator", "ini-inside-block");
    3572             : 
    3573             :         {
    3574           1 :             std::ofstream config_file;
    3575           1 :             config_file.open(SNAP_CATCH2_NAMESPACE::g_config_filename, std::ios_base::out | std::ios_base::binary | std::ios_base::trunc);
    3576           1 :             CATCH_REQUIRE(config_file.good());
    3577             :             config_file <<
    3578             :                 "# Auto-generated\n"
    3579             :                 "[colors]\n"
    3580             :                 "b=red\n"
    3581             :                 "c=blue\n"
    3582             :                 "\n"
    3583             :                 "block {\n"
    3584             :                 "  b = block data\n"
    3585             :                 "  f = filename\n"
    3586             :                 "  [sizes]\n"       // <-- INI section inside a block not allowed
    3587             :                 "  q=1000\n"
    3588             :                 "  r=9999\n"
    3589           1 :                 "}\n"
    3590             :             ;
    3591           1 :         }
    3592             : 
    3593           1 :         advgetopt::conf_file_setup setup(SNAP_CATCH2_NAMESPACE::g_config_filename
    3594             :                             , advgetopt::line_continuation_t::line_continuation_unix
    3595             :                             , advgetopt::ASSIGNMENT_OPERATOR_EQUAL
    3596             :                             , advgetopt::COMMENT_SHELL
    3597           1 :                             , advgetopt::SECTION_OPERATOR_BLOCK | advgetopt::SECTION_OPERATOR_INI_FILE);
    3598             : 
    3599           1 :         CATCH_REQUIRE(setup.is_valid());
    3600           1 :         CATCH_REQUIRE(setup.get_line_continuation() == advgetopt::line_continuation_t::line_continuation_unix);
    3601           1 :         CATCH_REQUIRE(setup.get_assignment_operator() == advgetopt::ASSIGNMENT_OPERATOR_EQUAL);
    3602           1 :         CATCH_REQUIRE(setup.get_comment() == advgetopt::COMMENT_SHELL);
    3603           1 :         CATCH_REQUIRE(setup.get_section_operator() == (advgetopt::SECTION_OPERATOR_BLOCK | advgetopt::SECTION_OPERATOR_INI_FILE));
    3604             : 
    3605           1 :         std::unique_ptr<char, decltype(&::free)> fn(realpath(SNAP_CATCH2_NAMESPACE::g_config_filename.c_str(), nullptr), &::free);
    3606           2 :         SNAP_CATCH2_NAMESPACE::push_expected_log(
    3607             :                       "error: `[...]` sections can't be used within a `section"
    3608             :                       " { ... }` on line 9 from configuration file \""
    3609           2 :                     + std::string(fn.get())
    3610           3 :                     + "\".");
    3611           1 :         advgetopt::conf_file::pointer_t file(advgetopt::conf_file::get_conf_file(setup));
    3612           1 :         SNAP_CATCH2_NAMESPACE::expected_logs_stack_is_empty();
    3613             : 
    3614           1 :         CATCH_REQUIRE(file->get_setup().get_config_url() == setup.get_config_url());
    3615           1 :         CATCH_REQUIRE(file->get_errno() == 0);
    3616             : 
    3617           1 :         advgetopt::conf_file::sections_t sections(file->get_sections());
    3618           1 :         CATCH_REQUIRE(sections.size() == 2);
    3619           1 :         CATCH_REQUIRE(sections.find("colors") != sections.end());
    3620           1 :         CATCH_REQUIRE(sections.find("colors::block") != sections.end());
    3621             : 
    3622           1 :         CATCH_REQUIRE(file->get_parameters().size() == 6);
    3623             : 
    3624           1 :         CATCH_REQUIRE(file->has_parameter("colors::b"));
    3625           1 :         CATCH_REQUIRE(file->has_parameter("colors::c"));
    3626           1 :         CATCH_REQUIRE(file->has_parameter("colors::block::b"));
    3627           1 :         CATCH_REQUIRE(file->has_parameter("colors::block::f"));
    3628           1 :         CATCH_REQUIRE(file->has_parameter("colors::block::q"));
    3629           1 :         CATCH_REQUIRE(file->has_parameter("colors::block::r"));
    3630             : 
    3631           1 :         CATCH_REQUIRE(file->get_parameter("colors::b") == "red");
    3632           1 :         CATCH_REQUIRE(file->get_parameter("colors::c") == "blue");
    3633           1 :         CATCH_REQUIRE(file->get_parameter("colors::block::b") == "block data");
    3634           1 :         CATCH_REQUIRE(file->get_parameter("colors::block::f") == "filename");
    3635           1 :         CATCH_REQUIRE(file->get_parameter("colors::block::q") == "1000");
    3636           1 :         CATCH_REQUIRE(file->get_parameter("colors::block::r") == "9999");
    3637           1 :     }
    3638           9 :     CATCH_END_SECTION()
    3639           9 : }
    3640             : 
    3641             : 
    3642           5 : CATCH_TEST_CASE("invalid_field_name", "[config][getopt][invalid]")
    3643             : {
    3644           5 :     CATCH_START_SECTION("invalid_field_name: empty field name")
    3645             :     {
    3646           1 :         SNAP_CATCH2_NAMESPACE::init_tmp_dir("invalid-variable-name", "name-missing");
    3647             : 
    3648             :         {
    3649           1 :             std::ofstream config_file;
    3650           1 :             config_file.open(SNAP_CATCH2_NAMESPACE::g_config_filename, std::ios_base::out | std::ios_base::binary | std::ios_base::trunc);
    3651           1 :             CATCH_REQUIRE(config_file.good());
    3652             :             config_file <<
    3653             :                 "# Auto-generated\n"
    3654             :                 "=color\n"                  // <-- name missing
    3655             :                 "a..b=red\n"
    3656           1 :                 "a.b.c=142\n"
    3657             :             ;
    3658           1 :         }
    3659             : 
    3660           1 :         advgetopt::conf_file_setup setup(SNAP_CATCH2_NAMESPACE::g_config_filename
    3661             :                             , advgetopt::line_continuation_t::line_continuation_unix
    3662             :                             , advgetopt::ASSIGNMENT_OPERATOR_EQUAL
    3663             :                             , advgetopt::COMMENT_SHELL
    3664           1 :                             , advgetopt::SECTION_OPERATOR_C);
    3665             : 
    3666           1 :         CATCH_REQUIRE(setup.is_valid());
    3667           1 :         CATCH_REQUIRE(setup.get_line_continuation() == advgetopt::line_continuation_t::line_continuation_unix);
    3668           1 :         CATCH_REQUIRE(setup.get_assignment_operator() == advgetopt::ASSIGNMENT_OPERATOR_EQUAL);
    3669           1 :         CATCH_REQUIRE(setup.get_comment() == advgetopt::COMMENT_SHELL);
    3670           1 :         CATCH_REQUIRE(setup.get_section_operator() == advgetopt::SECTION_OPERATOR_C);
    3671             : 
    3672           1 :         std::unique_ptr<char, decltype(&::free)> fn(realpath(SNAP_CATCH2_NAMESPACE::g_config_filename.c_str(), nullptr), &::free);
    3673           2 :         SNAP_CATCH2_NAMESPACE::push_expected_log(
    3674             :                       "error: no option name in \"=color\""
    3675             :                       " on line 2 from configuration file \""
    3676           2 :                     + std::string(fn.get())
    3677           3 :                     + "\", missing name before the assignment operator?");
    3678           1 :         advgetopt::conf_file::pointer_t file(advgetopt::conf_file::get_conf_file(setup));
    3679           1 :         SNAP_CATCH2_NAMESPACE::expected_logs_stack_is_empty();
    3680             : 
    3681           1 :         CATCH_REQUIRE(file->get_setup().get_config_url() == setup.get_config_url());
    3682           1 :         CATCH_REQUIRE(file->get_errno() == 0);
    3683             : 
    3684           1 :         advgetopt::conf_file::sections_t sections(file->get_sections());
    3685           1 :         CATCH_REQUIRE(sections.size() == 2);
    3686           1 :         CATCH_REQUIRE(sections.find("a")    != sections.end());
    3687           1 :         CATCH_REQUIRE(sections.find("a::b") != sections.end());
    3688             : 
    3689           1 :         CATCH_REQUIRE(file->get_parameters().size() == 2);
    3690             : 
    3691           1 :         CATCH_REQUIRE(file->has_parameter("a::b"));
    3692           1 :         CATCH_REQUIRE(file->has_parameter("a::b::c"));
    3693             : 
    3694           1 :         CATCH_REQUIRE(file->get_parameter("a::b") == "red");
    3695           1 :         CATCH_REQUIRE(file->get_parameter("a::b::c") == "142");
    3696           1 :     }
    3697           5 :     CATCH_END_SECTION()
    3698             : 
    3699           5 :     CATCH_START_SECTION("empty variable name after section name")
    3700             :     {
    3701           1 :         SNAP_CATCH2_NAMESPACE::init_tmp_dir("invalid-variable-name", "section-and-name-missing");
    3702             : 
    3703             :         {
    3704           1 :             std::ofstream config_file;
    3705           1 :             config_file.open(SNAP_CATCH2_NAMESPACE::g_config_filename, std::ios_base::out | std::ios_base::binary | std::ios_base::trunc);
    3706           1 :             CATCH_REQUIRE(config_file.good());
    3707             :             config_file <<
    3708             :                 "# Auto-generated\n"
    3709             :                 "a..b=red\n"
    3710             :                 "a.b.=color\n"                  // <-- name missing after section name
    3711           1 :                 "a.b.c=142\n"
    3712             :             ;
    3713           1 :         }
    3714             : 
    3715           1 :         advgetopt::conf_file_setup setup(SNAP_CATCH2_NAMESPACE::g_config_filename
    3716             :                             , advgetopt::line_continuation_t::line_continuation_unix
    3717             :                             , advgetopt::ASSIGNMENT_OPERATOR_EQUAL
    3718             :                             , advgetopt::COMMENT_SHELL
    3719           1 :                             , advgetopt::SECTION_OPERATOR_C);
    3720             : 
    3721           1 :         CATCH_REQUIRE(setup.is_valid());
    3722           1 :         CATCH_REQUIRE(setup.get_line_continuation() == advgetopt::line_continuation_t::line_continuation_unix);
    3723           1 :         CATCH_REQUIRE(setup.get_assignment_operator() == advgetopt::ASSIGNMENT_OPERATOR_EQUAL);
    3724           1 :         CATCH_REQUIRE(setup.get_comment() == advgetopt::COMMENT_SHELL);
    3725           1 :         CATCH_REQUIRE(setup.get_section_operator() == advgetopt::SECTION_OPERATOR_C);
    3726             : 
    3727           1 :         SNAP_CATCH2_NAMESPACE::push_expected_log(
    3728             :                       "error: option name \"a.b.\" cannot end with a section operator or be empty.");
    3729           1 :         advgetopt::conf_file::pointer_t file(advgetopt::conf_file::get_conf_file(setup));
    3730           1 :         SNAP_CATCH2_NAMESPACE::expected_logs_stack_is_empty();
    3731             : 
    3732           1 :         CATCH_REQUIRE(file->get_setup().get_config_url() == setup.get_config_url());
    3733           1 :         CATCH_REQUIRE(file->get_errno() == 0);
    3734             : 
    3735           1 :         advgetopt::conf_file::sections_t sections(file->get_sections());
    3736           1 :         CATCH_REQUIRE(sections.size() == 2);
    3737           1 :         CATCH_REQUIRE(sections.find("a") != sections.end());
    3738           1 :         CATCH_REQUIRE(sections.find("a::b") != sections.end());
    3739             : 
    3740           1 :         CATCH_REQUIRE(file->get_parameters().size() == 2);
    3741             : 
    3742           1 :         CATCH_REQUIRE(file->has_parameter("a::b"));
    3743           1 :         CATCH_REQUIRE(file->has_parameter("a::b::c"));
    3744             : 
    3745           1 :         CATCH_REQUIRE(file->get_parameter("a::b") == "red");
    3746           1 :         CATCH_REQUIRE(file->get_parameter("a::b::c") == "142");
    3747           1 :     }
    3748           5 :     CATCH_END_SECTION()
    3749             : 
    3750           5 :     CATCH_START_SECTION("variable name starts with a dash")
    3751             :     {
    3752           1 :         SNAP_CATCH2_NAMESPACE::init_tmp_dir("invalid-variable-name", "dash-name");
    3753             : 
    3754             :         {
    3755           1 :             std::ofstream config_file;
    3756           1 :             config_file.open(SNAP_CATCH2_NAMESPACE::g_config_filename, std::ios_base::out | std::ios_base::binary | std::ios_base::trunc);
    3757           1 :             CATCH_REQUIRE(config_file.good());
    3758             :             config_file <<
    3759             :                 "# Auto-generated\n"
    3760             :                 "a=color\n"
    3761             :                 "-bad-dash=reddish\n"            // <-- name starts with '-'
    3762           1 :                 "size=412\n"
    3763             :             ;
    3764           1 :         }
    3765             : 
    3766           1 :         advgetopt::conf_file_setup setup(SNAP_CATCH2_NAMESPACE::g_config_filename
    3767             :                             , advgetopt::line_continuation_t::line_continuation_unix
    3768             :                             , advgetopt::ASSIGNMENT_OPERATOR_EQUAL
    3769             :                             , advgetopt::COMMENT_SHELL
    3770           1 :                             , advgetopt::SECTION_OPERATOR_C);
    3771             : 
    3772           1 :         CATCH_REQUIRE(setup.is_valid());
    3773           1 :         CATCH_REQUIRE(setup.get_line_continuation() == advgetopt::line_continuation_t::line_continuation_unix);
    3774           1 :         CATCH_REQUIRE(setup.get_assignment_operator() == advgetopt::ASSIGNMENT_OPERATOR_EQUAL);
    3775           1 :         CATCH_REQUIRE(setup.get_comment() == advgetopt::COMMENT_SHELL);
    3776           1 :         CATCH_REQUIRE(setup.get_section_operator() == advgetopt::SECTION_OPERATOR_C);
    3777             : 
    3778           1 :         std::unique_ptr<char, decltype(&::free)> fn(realpath(SNAP_CATCH2_NAMESPACE::g_config_filename.c_str(), nullptr), &::free);
    3779           2 :         SNAP_CATCH2_NAMESPACE::push_expected_log(
    3780             :                       "error: option names in configuration files cannot"
    3781             :                       " start with a dash or an underscore in"
    3782             :                       " \"-bad-dash=reddish\" on line 3 from configuration file \""
    3783           2 :                     + std::string(fn.get())
    3784           3 :                     + "\".");
    3785           1 :         advgetopt::conf_file::pointer_t file(advgetopt::conf_file::get_conf_file(setup));
    3786           1 :         SNAP_CATCH2_NAMESPACE::expected_logs_stack_is_empty();
    3787             : 
    3788           1 :         CATCH_REQUIRE(file->get_setup().get_config_url() == setup.get_config_url());
    3789           1 :         CATCH_REQUIRE(file->get_errno() == 0);
    3790             : 
    3791           1 :         CATCH_REQUIRE(file->get_sections().empty());
    3792             : 
    3793           1 :         CATCH_REQUIRE(file->get_parameters().size() == 2);
    3794             : 
    3795           1 :         CATCH_REQUIRE(file->has_parameter("a"));
    3796           1 :         CATCH_REQUIRE(file->has_parameter("size"));
    3797             : 
    3798           1 :         CATCH_REQUIRE(file->get_parameter("a") == "color");
    3799           1 :         CATCH_REQUIRE(file->get_parameter("size") == "412");
    3800           1 :     }
    3801           5 :     CATCH_END_SECTION()
    3802             : 
    3803           5 :     CATCH_START_SECTION("variable name starts with an underscore")
    3804             :     {
    3805           1 :         SNAP_CATCH2_NAMESPACE::init_tmp_dir("invalid-variable-name", "underscore-name");
    3806             : 
    3807             :         {
    3808           1 :             std::ofstream config_file;
    3809           1 :             config_file.open(SNAP_CATCH2_NAMESPACE::g_config_filename, std::ios_base::out | std::ios_base::binary | std::ios_base::trunc);
    3810           1 :             CATCH_REQUIRE(config_file.good());
    3811             :             config_file <<
    3812             :                 "# Auto-generated\n"
    3813             :                 "a_variable=color\n"
    3814             :                 "_bad_underscore=reddish\n"        // <-- name starts with '_'
    3815           1 :                 "pos_and_size=412x33+32-18\n"
    3816             :             ;
    3817           1 :         }
    3818             : 
    3819           1 :         advgetopt::conf_file_setup setup(SNAP_CATCH2_NAMESPACE::g_config_filename
    3820             :                             , advgetopt::line_continuation_t::line_continuation_unix
    3821             :                             , advgetopt::ASSIGNMENT_OPERATOR_EQUAL
    3822             :                             , advgetopt::COMMENT_SHELL
    3823           1 :                             , advgetopt::SECTION_OPERATOR_C);
    3824             : 
    3825           1 :         CATCH_REQUIRE(setup.is_valid());
    3826           1 :         CATCH_REQUIRE(setup.get_line_continuation() == advgetopt::line_continuation_t::line_continuation_unix);
    3827           1 :         CATCH_REQUIRE(setup.get_assignment_operator() == advgetopt::ASSIGNMENT_OPERATOR_EQUAL);
    3828           1 :         CATCH_REQUIRE(setup.get_comment() == advgetopt::COMMENT_SHELL);
    3829           1 :         CATCH_REQUIRE(setup.get_section_operator() == advgetopt::SECTION_OPERATOR_C);
    3830             : 
    3831           1 :         std::unique_ptr<char, decltype(&::free)> fn(realpath(SNAP_CATCH2_NAMESPACE::g_config_filename.c_str(), nullptr), &::free);
    3832           2 :         SNAP_CATCH2_NAMESPACE::push_expected_log(
    3833             :                       "error: option names in configuration files cannot"
    3834             :                       " start with a dash or an underscore in"
    3835             :                       " \"_bad_underscore=reddish\" on line 3 from configuration file \""
    3836           2 :                     + std::string(fn.get())
    3837           3 :                     + "\".");
    3838           1 :         advgetopt::conf_file::pointer_t file(advgetopt::conf_file::get_conf_file(setup));
    3839           1 :         SNAP_CATCH2_NAMESPACE::expected_logs_stack_is_empty();
    3840             : 
    3841           1 :         CATCH_REQUIRE(file->get_setup().get_config_url() == setup.get_config_url());
    3842           1 :         CATCH_REQUIRE(file->get_errno() == 0);
    3843             : 
    3844           1 :         CATCH_REQUIRE(file->get_sections().empty());
    3845             : 
    3846           1 :         CATCH_REQUIRE(file->get_parameters().size() == 2);
    3847             : 
    3848           1 :         CATCH_REQUIRE(file->has_parameter("a-variable"));
    3849           1 :         CATCH_REQUIRE(file->has_parameter("pos-and-size"));
    3850             : 
    3851           1 :         CATCH_REQUIRE(file->get_parameter("a-variable") == "color");
    3852           1 :         CATCH_REQUIRE(file->get_parameter("pos-and-size") == "412x33+32-18");
    3853           1 :     }
    3854           5 :     CATCH_END_SECTION()
    3855             : 
    3856           5 :     CATCH_START_SECTION("variable name with spaces")
    3857             :     {
    3858           1 :         SNAP_CATCH2_NAMESPACE::init_tmp_dir("invalid-variable-name", "name-space-more-name");
    3859             : 
    3860             :         {
    3861           1 :             std::ofstream config_file;
    3862           1 :             config_file.open(SNAP_CATCH2_NAMESPACE::g_config_filename, std::ios_base::out | std::ios_base::binary | std::ios_base::trunc);
    3863           1 :             CATCH_REQUIRE(config_file.good());
    3864             :             config_file <<
    3865             :                 "# Auto-generated\n"
    3866             :                 "a variable=color\n"
    3867             :                 "bad space=reddish\n"
    3868           1 :                 "pos and size=412x33+32-18\n"
    3869             :             ;
    3870           1 :         }
    3871             : 
    3872           1 :         advgetopt::conf_file_setup setup(SNAP_CATCH2_NAMESPACE::g_config_filename
    3873             :                             , advgetopt::line_continuation_t::line_continuation_unix
    3874             :                             , advgetopt::ASSIGNMENT_OPERATOR_EQUAL
    3875             :                             , advgetopt::COMMENT_SHELL
    3876           1 :                             , advgetopt::SECTION_OPERATOR_C);
    3877             : 
    3878           1 :         CATCH_REQUIRE(setup.is_valid());
    3879           1 :         CATCH_REQUIRE(setup.get_line_continuation() == advgetopt::line_continuation_t::line_continuation_unix);
    3880           1 :         CATCH_REQUIRE(setup.get_assignment_operator() == advgetopt::ASSIGNMENT_OPERATOR_EQUAL);
    3881           1 :         CATCH_REQUIRE(setup.get_comment() == advgetopt::COMMENT_SHELL);
    3882           1 :         CATCH_REQUIRE(setup.get_section_operator() == advgetopt::SECTION_OPERATOR_C);
    3883             : 
    3884           1 :         std::unique_ptr<char, decltype(&::free)> fn(realpath(SNAP_CATCH2_NAMESPACE::g_config_filename.c_str(), nullptr), &::free);
    3885           2 :         SNAP_CATCH2_NAMESPACE::push_expected_log(
    3886             :                       "error: option name from \"a variable=color\" on line"
    3887             :                       " 2 in configuration file \""
    3888           2 :                     + std::string(fn.get())
    3889           3 :                     + "\" cannot include a space, missing assignment operator?");
    3890           2 :         SNAP_CATCH2_NAMESPACE::push_expected_log(
    3891             :                       "error: option name from \"bad space=reddish\" on line"
    3892             :                       " 3 in configuration file \""
    3893           2 :                     + std::string(fn.get())
    3894           3 :                     + "\" cannot include a space, missing assignment operator?");
    3895           2 :         SNAP_CATCH2_NAMESPACE::push_expected_log(
    3896             :                       "error: option name from \"pos and size=412x33+32-18\" on line"
    3897             :                       " 4 in configuration file \""
    3898           2 :                     + std::string(fn.get())
    3899           3 :                     + "\" cannot include a space, missing assignment operator?");
    3900           1 :         advgetopt::conf_file::pointer_t file(advgetopt::conf_file::get_conf_file(setup));
    3901           1 :         SNAP_CATCH2_NAMESPACE::expected_logs_stack_is_empty();
    3902             : 
    3903           1 :         CATCH_REQUIRE(file->get_setup().get_config_url() == setup.get_config_url());
    3904           1 :         CATCH_REQUIRE(file->get_errno() == 0);
    3905             : 
    3906           1 :         CATCH_REQUIRE(file->get_sections().empty());
    3907             : 
    3908           1 :         CATCH_REQUIRE(file->get_parameters().empty());
    3909           1 :     }
    3910           5 :     CATCH_END_SECTION()
    3911           5 : }
    3912             : 
    3913             : 
    3914             : 
    3915             : // vim: ts=4 sw=4 et

Generated by: LCOV version 1.14

Snap C++ | List of projects | List of versions