https://gcc.gnu.org/g:28943c3c74cc320c009d124523029ce4b8368121
commit r16-4416-g28943c3c74cc320c009d124523029ce4b8368121 Author: Tomasz Kamiński <[email protected]> Date: Thu Oct 9 16:27:18 2025 +0200 libstdc++: Formatting tests for std::chrono compose types. This covers: * weekday_indexed, weekday_last * month_day, month_day_last, * month_weekday, month_weekday_last * year_month libstdc++-v3/ChangeLog: * testsuite/std/time/month_day/io.cc: New formatting tests. * testsuite/std/time/month_day_last/io.cc: Likewise. * testsuite/std/time/month_weekday/io.cc: Likewise. * testsuite/std/time/month_weekday_last/io.cc: Likewise. * testsuite/std/time/weekday_indexed/io.cc: Likewise. * testsuite/std/time/weekday_last/io.cc: Likewise. * testsuite/std/time/year_month/io.cc: Likewise. Reviewed-by: Patrick Palka <[email protected]> Signed-off-by: Tomasz Kamiński <[email protected]> Diff: --- libstdc++-v3/testsuite/std/time/month_day/io.cc | 41 +++++++++++++++++++++- .../testsuite/std/time/month_day_last/io.cc | 41 +++++++++++++++++++++- .../testsuite/std/time/month_weekday/io.cc | 41 +++++++++++++++++++++- .../testsuite/std/time/month_weekday_last/io.cc | 41 +++++++++++++++++++++- .../testsuite/std/time/weekday_indexed/io.cc | 41 +++++++++++++++++++++- libstdc++-v3/testsuite/std/time/weekday_last/io.cc | 41 +++++++++++++++++++++- libstdc++-v3/testsuite/std/time/year_month/io.cc | 41 +++++++++++++++++++++- 7 files changed, 280 insertions(+), 7 deletions(-) diff --git a/libstdc++-v3/testsuite/std/time/month_day/io.cc b/libstdc++-v3/testsuite/std/time/month_day/io.cc index 30aa58813560..c3ae180b32b7 100644 --- a/libstdc++-v3/testsuite/std/time/month_day/io.cc +++ b/libstdc++-v3/testsuite/std/time/month_day/io.cc @@ -22,6 +22,45 @@ test_ostream() VERIFY( ss.str() == "juil./27" ); } +void +test_format() +{ + using namespace std::chrono; + std::locale loc_fr(ISO_8859(15,fr_FR)); + + auto s = std::format("{:%b%%%B%t%m%n %d%%%e}", month(1)/day(3)); + VERIFY( s == "Jan%January\t01\n 03% 3" ); + s = std::format(loc_fr, "{:L%b%%%B%t%m%n %d%%%e}", month(1)/day(3)); + VERIFY( s == "janv.%janvier\t01\n 03% 3"); + + s = std::format("{0:%m/%d} {0}", month(10)/day(13)); + VERIFY( s == "10/13 Oct/13" ); + s = std::format("{0:%m/%d} {0}", month(13)/day(34)); + VERIFY( s == "13/34 13 is not a valid month/34 is not a valid day" ); + + std::string_view specs = "aAbBcCdDeFgGhHIjmMpqQrRSTuUVwWxXyYzZ"; + std::string_view my_specs = "bBdehm"; + for (char c : specs) + { + char fmt[] = { '{', ':', '%', c, '}' }; + try + { + auto md = month(1)/day(10); + (void) std::vformat(std::string_view(fmt, 5), std::make_format_args(md)); + // The call above should throw for any conversion-spec not in my_specs: + VERIFY(my_specs.find(c) != my_specs.npos); + } + catch (const std::format_error& e) + { + VERIFY(my_specs.find(c) == my_specs.npos); + std::string_view s = e.what(); + // Libstdc++-specific message: + VERIFY(s.find("format argument does not contain the information " + "required by the chrono-specs") != s.npos); + } + } +} + void test_parse() { @@ -102,6 +141,6 @@ test_parse() int main() { test_ostream(); - // TODO: test_format(); + test_format(); test_parse(); } diff --git a/libstdc++-v3/testsuite/std/time/month_day_last/io.cc b/libstdc++-v3/testsuite/std/time/month_day_last/io.cc index d15192c9b7a8..484a8d8a05e8 100644 --- a/libstdc++-v3/testsuite/std/time/month_day_last/io.cc +++ b/libstdc++-v3/testsuite/std/time/month_day_last/io.cc @@ -22,8 +22,47 @@ test_ostream() VERIFY( ss.str() == "juil./last" ); } +void +test_format() +{ + using namespace std::chrono; + std::locale loc_fr(ISO_8859(15,fr_FR)); + + auto s = std::format("{:%b%%%B%t%m%n}", month(3)/last); + VERIFY( s == "Mar%March\t03\n" ); + s = std::format(loc_fr, "{:L%b%%%B%t%m%n}", month(3)/last); + VERIFY( s == "mars%mars\t03\n"); + + s = std::format("{0:%m/last} {0}", month(4)/last); + VERIFY( s == "04/last Apr/last" ); + s = std::format("{0:%m/last} {0}", month(0)/last); + VERIFY( s == "00/last 0 is not a valid month/last" ); + + std::string_view specs = "aAbBcCdDeFgGhHIjmMpqQrRSTuUVwWxXyYzZ"; + std::string_view my_specs = "bBhm"; + for (char c : specs) + { + char fmt[] = { '{', ':', '%', c, '}' }; + try + { + auto mdl = month(1)/last; + (void) std::vformat(std::string_view(fmt, 5), std::make_format_args(mdl)); + // The call above should throw for any conversion-spec not in my_specs: + VERIFY(my_specs.find(c) != my_specs.npos); + } + catch (const std::format_error& e) + { + VERIFY(my_specs.find(c) == my_specs.npos); + std::string_view s = e.what(); + // Libstdc++-specific message: + VERIFY(s.find("format argument does not contain the information " + "required by the chrono-specs") != s.npos); + } + } +} + int main() { test_ostream(); - // TODO: test_format(); + test_format(); } diff --git a/libstdc++-v3/testsuite/std/time/month_weekday/io.cc b/libstdc++-v3/testsuite/std/time/month_weekday/io.cc index 183803071098..0c2dcafe6543 100644 --- a/libstdc++-v3/testsuite/std/time/month_weekday/io.cc +++ b/libstdc++-v3/testsuite/std/time/month_weekday/io.cc @@ -23,8 +23,47 @@ test_ostream() VERIFY( ss.str() == "juil./jeu.[4]" ); } +void +test_format() +{ + using namespace std::chrono; + std::locale loc_fr(ISO_8859(15,fr_FR)); + + auto s = std::format("{:%b%%%B%t%m%n %a%%%A%t%u%n%w}", month(5)/weekday(1)[2]); + VERIFY( s == "May%May\t05\n Mon%Monday\t1\n1" ); + s = std::format(loc_fr, "{:L%b%%%B%t%m%n %a%%%A%t%u%n%w}", month(5)/weekday(1)[2]); + VERIFY( s == "mai%mai\t05\n lun.%lundi\t1\n1"); + + s = std::format("{0:%m/%u[]} {0}", month(9)/weekday(0)[2]); + VERIFY( s == "09/7[] Sep/Sun[2]" ); + s = std::format("{0:%m/%u[]} {0}", month(111)/weekday(8)[0]); + VERIFY( s == "111/8[] 111 is not a valid month/8 is not a valid weekday[0 is not a valid index]" ); + + std::string_view specs = "aAbBcCdDeFgGhHIjmMpqQrRSTuUVwWxXyYzZ"; + std::string_view my_specs = "aAbBhmuw"; + for (char c : specs) + { + char fmt[] = { '{', ':', '%', c, '}' }; + try + { + auto mwi = month(1)/weekday(1)[1]; + (void) std::vformat(std::string_view(fmt, 5), std::make_format_args(mwi)); + // The call above should throw for any conversion-spec not in my_specs: + VERIFY(my_specs.find(c) != my_specs.npos); + } + catch (const std::format_error& e) + { + VERIFY(my_specs.find(c) == my_specs.npos); + std::string_view s = e.what(); + // Libstdc++-specific message: + VERIFY(s.find("format argument does not contain the information " + "required by the chrono-specs") != s.npos); + } + } +} + int main() { test_ostream(); - // TODO: test_format(); + test_format(); } diff --git a/libstdc++-v3/testsuite/std/time/month_weekday_last/io.cc b/libstdc++-v3/testsuite/std/time/month_weekday_last/io.cc index 6ba4d8ab084e..2c29258e1a86 100644 --- a/libstdc++-v3/testsuite/std/time/month_weekday_last/io.cc +++ b/libstdc++-v3/testsuite/std/time/month_weekday_last/io.cc @@ -23,8 +23,47 @@ test_ostream() VERIFY( ss.str() == "juil./jeu.[last]" ); } +void +test_format() +{ + using namespace std::chrono; + std::locale loc_fr(ISO_8859(15,fr_FR)); + + auto s = std::format("{:%b%%%B%t%m%n %a%%%A%t%u%n%w}", month(6)/weekday(2)[last]); + VERIFY( s == "Jun%June\t06\n Tue%Tuesday\t2\n2" ); + s = std::format(loc_fr, "{:L%b%%%B%t%m%n %a%%%A%t%u%n%w}", month(6)/weekday(2)[last]); + VERIFY( s == "juin%juin\t06\n mar.%mardi\t2\n2"); + + s = std::format("{0:%m/%w[last]} {0}", month(8)/weekday(7)[last]); + VERIFY( s == "08/0[last] Aug/Sun[last]" ); + s = std::format("{0:%m/%w[last]} {0}", month(70)/weekday(9)[last]); + VERIFY( s == "70/9[last] 70 is not a valid month/9 is not a valid weekday[last]" ); + + std::string_view specs = "aAbBcCdDeFgGhHIjmMpqQrRSTuUVwWxXyYzZ"; + std::string_view my_specs = "aAbBhmuw"; + for (char c : specs) + { + char fmt[] = { '{', ':', '%', c, '}' }; + try + { + auto mwl = month(1)/weekday(1)[last]; + (void) std::vformat(std::string_view(fmt, 5), std::make_format_args(mwl)); + // The call above should throw for any conversion-spec not in my_specs: + VERIFY(my_specs.find(c) != my_specs.npos); + } + catch (const std::format_error& e) + { + VERIFY(my_specs.find(c) == my_specs.npos); + std::string_view s = e.what(); + // Libstdc++-specific message: + VERIFY(s.find("format argument does not contain the information " + "required by the chrono-specs") != s.npos); + } + } +} + int main() { test_ostream(); - // TODO: test_format(); + test_format(); } diff --git a/libstdc++-v3/testsuite/std/time/weekday_indexed/io.cc b/libstdc++-v3/testsuite/std/time/weekday_indexed/io.cc index ca315de865b2..ae864192fa5f 100644 --- a/libstdc++-v3/testsuite/std/time/weekday_indexed/io.cc +++ b/libstdc++-v3/testsuite/std/time/weekday_indexed/io.cc @@ -22,8 +22,47 @@ test_ostream() VERIFY( ss.str() == "sam.[1]" ); } +void +test_format() +{ + using namespace std::chrono; + std::locale loc_fr(ISO_8859(15,fr_FR)); + + auto s = std::format("{:%a%%%A%t%u%n%w}", weekday(7)[3]); + VERIFY( s == "Sun%Sunday\t7\n0" ); + s = std::format(loc_fr, "{:L%a%%%A%t%u%n%w}", weekday(7)[3]); + VERIFY( s == "dim.%dimanche\t7\n0"); + + s = std::format("{0:%w[]} {0}", weekday(4)[4]); + VERIFY( s == "4[] Thu[4]" ); + s = std::format("{0:%w[]} {0}", weekday(10)[7]); + VERIFY( s == "10[] 10 is not a valid weekday[7 is not a valid index]" ); + + std::string_view specs = "aAbBcCdDeFgGhHIjmMpqQrRSTuUVwWxXyYzZ"; + std::string_view my_specs = "aAuw"; + for (char c : specs) + { + char fmt[] = { '{', ':', '%', c, '}' }; + try + { + auto wi = weekday(1)[1]; + (void) std::vformat(std::string_view(fmt, 5), std::make_format_args(wi)); + // The call above should throw for any conversion-spec not in my_specs: + VERIFY(my_specs.find(c) != my_specs.npos); + } + catch (const std::format_error& e) + { + VERIFY(my_specs.find(c) == my_specs.npos); + std::string_view s = e.what(); + // Libstdc++-specific message: + VERIFY(s.find("format argument does not contain the information " + "required by the chrono-specs") != s.npos); + } + } +} + int main() { test_ostream(); - // TODO: test_format(); + test_format(); } diff --git a/libstdc++-v3/testsuite/std/time/weekday_last/io.cc b/libstdc++-v3/testsuite/std/time/weekday_last/io.cc index 3b64c655617b..49cf0d5bddd9 100644 --- a/libstdc++-v3/testsuite/std/time/weekday_last/io.cc +++ b/libstdc++-v3/testsuite/std/time/weekday_last/io.cc @@ -22,8 +22,47 @@ test_ostream() VERIFY( ss.str() == "sam.[last]" ); } +void +test_format() +{ + using namespace std::chrono; + std::locale loc_fr(ISO_8859(15,fr_FR)); + + auto s = std::format("{:%a%%%A%t%u%n%w}", weekday(5)[last]); + VERIFY( s == "Fri%Friday\t5\n5" ); + s = std::format(loc_fr, "{:L%a%%%A%t%u%n%w}", weekday(5)[last]); + VERIFY( s == "ven.%vendredi\t5\n5"); + + s = std::format("{0:%w[last]} {0}", weekday(6)[last]); + VERIFY( s == "6[last] Sat[last]" ); + s = std::format("{0:%w[last]} {0}", weekday(9)[last]); + VERIFY( s == "9[last] 9 is not a valid weekday[last]" ); + + std::string_view specs = "aAbBcCdDeFgGhHIjmMpqQrRSTuUVwWxXyYzZ"; + std::string_view my_specs = "aAuw"; + for (char c : specs) + { + char fmt[] = { '{', ':', '%', c, '}' }; + try + { + auto wl = weekday(1)[last]; + (void) std::vformat(std::string_view(fmt, 5), std::make_format_args(wl)); + // The call above should throw for any conversion-spec not in my_specs: + VERIFY(my_specs.find(c) != my_specs.npos); + } + catch (const std::format_error& e) + { + VERIFY(my_specs.find(c) == my_specs.npos); + std::string_view s = e.what(); + // Libstdc++-specific message: + VERIFY(s.find("format argument does not contain the information " + "required by the chrono-specs") != s.npos); + } + } +} + int main() { test_ostream(); - // TODO: test_format(); + test_format(); } diff --git a/libstdc++-v3/testsuite/std/time/year_month/io.cc b/libstdc++-v3/testsuite/std/time/year_month/io.cc index 7bb3442e2990..3392eb334bf7 100644 --- a/libstdc++-v3/testsuite/std/time/year_month/io.cc +++ b/libstdc++-v3/testsuite/std/time/year_month/io.cc @@ -22,6 +22,45 @@ test_ostream() VERIFY( ss.str() == "2023/juil." ); } +void +test_format() +{ + using namespace std::chrono; + std::locale loc_fr(ISO_8859(15,fr_FR)); + + auto s = std::format("{:%C%%%y\t%Y %b%%%B%t%m%n}", year(2019)/month(4)); + VERIFY( s == "20%19\t2019 Apr%April\t04\n" ); + s = std::format(loc_fr, "{:L%C%%%y\t%Y %b%%%B%t%m%n}", year(2019)/month(4)); + VERIFY( s == "20%19\t2019 avril%avril\t04\n"); + + s = std::format("{0:%Y/%m} {0}", year(2018)/month(2)); + VERIFY( s == "2018/02 2018/Feb" ); + s = std::format("{0:%Y/%m} {0}", year(-32768)/month(15)); + VERIFY( s == "-32768/15 -32768 is not a valid year/15 is not a valid month" ); + + std::string_view specs = "aAbBcCdDeFgGhHIjmMpqQrRSTuUVwWxXyYzZ"; + std::string_view my_specs = "CbBhmyY"; + for (char c : specs) + { + char fmt[] = { '{', ':', '%', c, '}' }; + try + { + auto ym = year(2013)/month(1); + (void) std::vformat(std::string_view(fmt, 5), std::make_format_args(ym)); + // The call above should throw for any conversion-spec not in my_specs: + VERIFY(my_specs.find(c) != my_specs.npos); + } + catch (const std::format_error& e) + { + VERIFY(my_specs.find(c) == my_specs.npos); + std::string_view s = e.what(); + // Libstdc++-specific message: + VERIFY(s.find("format argument does not contain the information " + "required by the chrono-specs") != s.npos); + } + } +} + void test_parse() { @@ -73,6 +112,6 @@ test_parse() int main() { test_ostream(); - // TODO: test_format(); + test_format(); test_parse(); }
