LCOV - code coverage report
Current view: top level - src/test - util_string_tests.cpp (source / functions) Coverage Total Hit
Test: total_coverage.info Lines: 100.0 % 94 94
Test Date: 2025-01-19 05:08:01 Functions: 100.0 % 24 24
Branches: 40.8 % 174 71

             Branch data     Line data    Source code
       1                 :             : // Copyright (c) 2024-present The Bitcoin Core developers
       2                 :             : // Distributed under the MIT software license, see the accompanying
       3                 :             : // file COPYING or http://www.opensource.org/licenses/mit-license.php.
       4                 :             : 
       5                 :             : #include <util/string.h>
       6                 :             : 
       7                 :             : #include <boost/test/unit_test.hpp>
       8                 :             : #include <test/util/setup_common.h>
       9                 :             : 
      10                 :             : using namespace util;
      11                 :             : using util::detail::CheckNumFormatSpecifiers;
      12                 :             : 
      13                 :             : BOOST_AUTO_TEST_SUITE(util_string_tests)
      14                 :             : 
      15                 :             : template <unsigned NumArgs>
      16                 :          42 : void TfmFormatZeroes(const std::string& fmt)
      17                 :             : {
      18                 :         124 :     std::apply([&](auto... args) {
      19                 :          42 :         (void)tfm::format(tfm::RuntimeFormat{fmt}, args...);
      20                 :             :     }, std::array<int, NumArgs>{});
      21                 :          40 : }
      22                 :             : 
      23                 :             : // Helper to allow compile-time sanity checks while providing the number of
      24                 :             : // args directly. Normally PassFmt<sizeof...(Args)> would be used.
      25                 :             : template <unsigned NumArgs>
      26                 :          40 : void PassFmt(ConstevalFormatString<NumArgs> fmt)
      27                 :             : {
      28                 :             :     // Execute compile-time check again at run-time to get code coverage stats
      29   [ +  -  +  -  :          80 :     BOOST_CHECK_NO_THROW(CheckNumFormatSpecifiers<NumArgs>(fmt.fmt));
          +  -  +  -  -  
                -  -  - ]
      30                 :             : 
      31                 :             :     // If ConstevalFormatString didn't throw above, make sure tinyformat doesn't
      32                 :             :     // throw either for the same format string and parameter count combination.
      33                 :             :     // Proves that we have some extent of protection from runtime errors
      34                 :             :     // (tinyformat may still throw for some type mismatches).
      35   [ +  -  +  -  :         120 :     BOOST_CHECK_NO_THROW(TfmFormatZeroes<NumArgs>(fmt.fmt));
          +  -  +  -  -  
                -  -  - ]
      36                 :          40 : }
      37                 :             : template <unsigned WrongNumArgs>
      38                 :          32 : void FailFmtWithError(const char* wrong_fmt, std::string_view error)
      39                 :             : {
      40   [ +  -  -  +  :          64 :     BOOST_CHECK_EXCEPTION(CheckNumFormatSpecifiers<WrongNumArgs>(wrong_fmt), const char*, HasReason{error});
          -  -  -  -  -  
          +  +  -  +  -  
                   +  - ]
      41                 :          32 : }
      42                 :             : 
      43   [ +  -  +  -  :           7 : BOOST_AUTO_TEST_CASE(ConstevalFormatString_NumSpec)
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
                      - ]
      44                 :             : {
      45                 :           1 :     PassFmt<0>("");
      46                 :           1 :     PassFmt<0>("%%");
      47                 :           1 :     PassFmt<1>("%s");
      48                 :           1 :     PassFmt<1>("%c");
      49                 :           1 :     PassFmt<0>("%%s");
      50                 :           1 :     PassFmt<0>("s%%");
      51                 :           1 :     PassFmt<1>("%%%s");
      52                 :           1 :     PassFmt<1>("%s%%");
      53                 :           1 :     PassFmt<0>(" 1$s");
      54                 :           1 :     PassFmt<1>("%1$s");
      55                 :           1 :     PassFmt<1>("%1$s%1$s");
      56                 :           1 :     PassFmt<2>("%2$s");
      57                 :           1 :     PassFmt<2>("%2$s 4$s %2$s");
      58                 :           1 :     PassFmt<129>("%129$s 999$s %2$s");
      59                 :           1 :     PassFmt<1>("%02d");
      60                 :           1 :     PassFmt<1>("%+2s");
      61                 :           1 :     PassFmt<1>("%.6i");
      62                 :           1 :     PassFmt<1>("%5.2f");
      63                 :           1 :     PassFmt<1>("%5.f");
      64                 :           1 :     PassFmt<1>("%.f");
      65                 :           1 :     PassFmt<1>("%#x");
      66                 :           1 :     PassFmt<1>("%1$5i");
      67                 :           1 :     PassFmt<1>("%1$-5i");
      68                 :           1 :     PassFmt<1>("%1$.5i");
      69                 :             :     // tinyformat accepts almost any "type" spec, even '%', or '_', or '\n'.
      70                 :           1 :     PassFmt<1>("%123%");
      71                 :           1 :     PassFmt<1>("%123%s");
      72                 :           1 :     PassFmt<1>("%_");
      73                 :           1 :     PassFmt<1>("%\n");
      74                 :             : 
      75                 :           1 :     PassFmt<2>("%*c");
      76                 :           1 :     PassFmt<2>("%+*c");
      77                 :           1 :     PassFmt<2>("%.*f");
      78                 :           1 :     PassFmt<3>("%*.*f");
      79                 :           1 :     PassFmt<3>("%2$*3$d");
      80                 :           1 :     PassFmt<3>("%2$*3$.9d");
      81                 :           1 :     PassFmt<3>("%2$.*3$d");
      82                 :           1 :     PassFmt<3>("%2$9.*3$d");
      83                 :           1 :     PassFmt<3>("%2$+9.*3$d");
      84                 :           1 :     PassFmt<4>("%3$*2$.*4$f");
      85                 :             : 
      86                 :             :     // Make sure multiple flag characters "- 0+" are accepted
      87                 :           1 :     PassFmt<3>("'%- 0+*.*f'");
      88                 :           1 :     PassFmt<3>("'%1$- 0+*3$.*2$f'");
      89                 :             : 
      90                 :           1 :     auto err_mix{"Format specifiers must be all positional or all non-positional!"};
      91                 :           1 :     FailFmtWithError<1>("%s%1$s", err_mix);
      92                 :           1 :     FailFmtWithError<2>("%2$*d", err_mix);
      93                 :           1 :     FailFmtWithError<2>("%*2$d", err_mix);
      94                 :           1 :     FailFmtWithError<2>("%.*3$d", err_mix);
      95                 :           1 :     FailFmtWithError<2>("%2$.*d", err_mix);
      96                 :             : 
      97                 :           1 :     auto err_num{"Format specifier count must match the argument count!"};
      98                 :           1 :     FailFmtWithError<1>("", err_num);
      99                 :           1 :     FailFmtWithError<0>("%s", err_num);
     100                 :           1 :     FailFmtWithError<2>("%s", err_num);
     101                 :           1 :     FailFmtWithError<0>("%1$s", err_num);
     102                 :           1 :     FailFmtWithError<2>("%1$s", err_num);
     103                 :           1 :     FailFmtWithError<1>("%*c", err_num);
     104                 :             : 
     105                 :           1 :     auto err_0_pos{"Positional format specifier must have position of at least 1"};
     106                 :           1 :     FailFmtWithError<1>("%$s", err_0_pos);
     107                 :           1 :     FailFmtWithError<1>("%$", err_0_pos);
     108                 :           1 :     FailFmtWithError<0>("%0$", err_0_pos);
     109                 :           1 :     FailFmtWithError<0>("%0$s", err_0_pos);
     110                 :           1 :     FailFmtWithError<2>("%2$*$d", err_0_pos);
     111                 :           1 :     FailFmtWithError<2>("%2$*0$d", err_0_pos);
     112                 :           1 :     FailFmtWithError<3>("%3$*2$.*$f", err_0_pos);
     113                 :           1 :     FailFmtWithError<3>("%3$*2$.*0$f", err_0_pos);
     114                 :             : 
     115                 :           1 :     auto err_term{"Format specifier incorrectly terminated by end of string"};
     116                 :           1 :     FailFmtWithError<1>("%", err_term);
     117                 :           1 :     FailFmtWithError<1>("%9", err_term);
     118                 :           1 :     FailFmtWithError<1>("%9.", err_term);
     119                 :           1 :     FailFmtWithError<1>("%9.9", err_term);
     120                 :           1 :     FailFmtWithError<1>("%*", err_term);
     121                 :           1 :     FailFmtWithError<1>("%+*", err_term);
     122                 :           1 :     FailFmtWithError<1>("%.*", err_term);
     123                 :           1 :     FailFmtWithError<1>("%9.*", err_term);
     124                 :           1 :     FailFmtWithError<1>("%1$", err_term);
     125                 :           1 :     FailFmtWithError<1>("%1$9", err_term);
     126                 :           1 :     FailFmtWithError<2>("%1$*2$", err_term);
     127                 :           1 :     FailFmtWithError<2>("%1$.*2$", err_term);
     128                 :           1 :     FailFmtWithError<2>("%1$9.*2$", err_term);
     129                 :             : 
     130                 :             :     // Non-parity between tinyformat and ConstevalFormatString.
     131                 :             :     // tinyformat throws but ConstevalFormatString does not.
     132   [ +  -  -  +  :           2 :     BOOST_CHECK_EXCEPTION(tfm::format(ConstevalFormatString<1>{"%n"}, 0), tfm::format_error,
          -  -  -  -  -  
          +  +  -  +  -  
                   +  - ]
     133                 :             :         HasReason{"tinyformat: %n conversion spec not supported"});
     134   [ +  -  -  +  :           2 :     BOOST_CHECK_EXCEPTION(tfm::format(ConstevalFormatString<2>{"%*s"}, "hi", "hi"), tfm::format_error,
          -  -  -  -  -  
          +  +  -  +  -  
                   +  - ]
     135                 :             :         HasReason{"tinyformat: Cannot convert from argument type to integer for use as variable width or precision"});
     136   [ +  -  -  +  :           2 :     BOOST_CHECK_EXCEPTION(tfm::format(ConstevalFormatString<2>{"%.*s"}, "hi", "hi"), tfm::format_error,
          -  -  -  -  -  
          +  +  -  +  -  
                   +  - ]
     137                 :             :         HasReason{"tinyformat: Cannot convert from argument type to integer for use as variable width or precision"});
     138                 :             : 
     139                 :             :     // Ensure that tinyformat throws if format string contains wrong number
     140                 :             :     // of specifiers. PassFmt relies on this to verify tinyformat successfully
     141                 :             :     // formats the strings, and will need to be updated if tinyformat is changed
     142                 :             :     // not to throw on failure.
     143   [ +  -  +  -  :           3 :     BOOST_CHECK_EXCEPTION(TfmFormatZeroes<2>("%s"), tfm::format_error,
          -  +  -  -  -  
          -  -  +  +  -  
             +  -  +  - ]
     144                 :             :         HasReason{"tinyformat: Not enough conversion specifiers in format string"});
     145   [ +  -  +  -  :           3 :     BOOST_CHECK_EXCEPTION(TfmFormatZeroes<1>("%s %s"), tfm::format_error,
          -  +  -  -  -  
          -  -  +  +  -  
             +  -  +  - ]
     146                 :             :         HasReason{"tinyformat: Too many conversion specifiers in format string"});
     147                 :           1 : }
     148                 :             : 
     149                 :             : BOOST_AUTO_TEST_SUITE_END()
        

Generated by: LCOV version 2.0-1