|
8 | 8 | #include <test/util/setup_common.h>
|
9 | 9 |
|
10 | 10 | using namespace util;
|
| 11 | +using util::detail::CheckNumFormatSpecifiers; |
11 | 12 |
|
12 | 13 | BOOST_AUTO_TEST_SUITE(util_string_tests)
|
13 | 14 |
|
| 15 | +template <unsigned NumArgs> |
| 16 | +void TfmFormatZeroes(const std::string& fmt) |
| 17 | +{ |
| 18 | + std::apply([&](auto... args) { |
| 19 | + (void)tfm::format(fmt, args...); |
| 20 | + }, std::array<int, NumArgs>{}); |
| 21 | +} |
| 22 | + |
14 | 23 | // Helper to allow compile-time sanity checks while providing the number of
|
15 | 24 | // args directly. Normally PassFmt<sizeof...(Args)> would be used.
|
16 | 25 | template <unsigned NumArgs>
|
17 |
| -inline void PassFmt(util::ConstevalFormatString<NumArgs> fmt) |
| 26 | +void PassFmt(ConstevalFormatString<NumArgs> fmt) |
18 | 27 | {
|
19 | 28 | // Execute compile-time check again at run-time to get code coverage stats
|
20 |
| - util::detail::CheckNumFormatSpecifiers<NumArgs>(fmt.fmt); |
| 29 | + 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 | + BOOST_CHECK_NO_THROW(TfmFormatZeroes<NumArgs>(fmt.fmt)); |
21 | 36 | }
|
22 | 37 | template <unsigned WrongNumArgs>
|
23 |
| -inline void FailFmtWithError(const char* wrong_fmt, std::string_view error) |
| 38 | +void FailFmtWithError(const char* wrong_fmt, std::string_view error) |
24 | 39 | {
|
25 |
| - BOOST_CHECK_EXCEPTION(util::detail::CheckNumFormatSpecifiers<WrongNumArgs>(wrong_fmt), const char*, HasReason{error}); |
| 40 | + BOOST_CHECK_EXCEPTION(CheckNumFormatSpecifiers<WrongNumArgs>(wrong_fmt), const char*, HasReason{error}); |
26 | 41 | }
|
27 | 42 |
|
28 | 43 | BOOST_AUTO_TEST_CASE(ConstevalFormatString_NumSpec)
|
29 | 44 | {
|
30 | 45 | PassFmt<0>("");
|
31 | 46 | PassFmt<0>("%%");
|
32 | 47 | PassFmt<1>("%s");
|
| 48 | + PassFmt<1>("%c"); |
33 | 49 | PassFmt<0>("%%s");
|
34 | 50 | PassFmt<0>("s%%");
|
35 | 51 | PassFmt<1>("%%%s");
|
@@ -110,6 +126,24 @@ BOOST_AUTO_TEST_CASE(ConstevalFormatString_NumSpec)
|
110 | 126 | FailFmtWithError<2>("%1$*2$", err_term);
|
111 | 127 | FailFmtWithError<2>("%1$.*2$", err_term);
|
112 | 128 | FailFmtWithError<2>("%1$9.*2$", err_term);
|
| 129 | + |
| 130 | + // Non-parity between tinyformat and ConstevalFormatString. |
| 131 | + // tinyformat throws but ConstevalFormatString does not. |
| 132 | + BOOST_CHECK_EXCEPTION(tfm::format(ConstevalFormatString<1>{"%n"}, 0), tfm::format_error, |
| 133 | + HasReason{"tinyformat: %n conversion spec not supported"}); |
| 134 | + 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 | + 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 | + BOOST_CHECK_EXCEPTION(TfmFormatZeroes<2>("%s"), tfm::format_error, |
| 144 | + HasReason{"tinyformat: Not enough conversion specifiers in format string"}); |
| 145 | + BOOST_CHECK_EXCEPTION(TfmFormatZeroes<1>("%s %s"), tfm::format_error, |
| 146 | + HasReason{"tinyformat: Too many conversion specifiers in format string"}); |
113 | 147 | }
|
114 | 148 |
|
115 | 149 | BOOST_AUTO_TEST_SUITE_END()
|
0 commit comments