From 49d4fee9940f5e1273cc0ae30da82df9c1437706 Mon Sep 17 00:00:00 2001 From: Mark de Wever Date: Thu, 15 Dec 2022 18:07:41 +0100 Subject: [PATCH] [libc++][format] Removes test redundancy. The format function test serve two purposes: - Test whether all format functions work in general. - Test whether all formatting rules are implemented correctly. At the moment the *pass.cpp tests do both. These tests are quite slow, while testing all rules for all functions doesn't add much coverage. There are two execution modi of the format functions: - run-time validation in the vformat functions. - compile-time validation in the other function. So instead of running all tests for all functions, they are only used for format.pass.cpp and vformat.pass.cpp still do all tests. The other tests do a smaller set of test, just to make sure they work in the basics. Running the format tests using one thread: - before 00:04:16 - after 00:02:14 The slow tests were also reported in https::llvm.org/PR58141 Also split a generic part of the test to a generic support header. This allows these parts to be reused in the range-based formatter tests. Reviewed By: #libc, ldionne Differential Revision: https://reviews.llvm.org/D140115 --- .../format/format.functions/format.locale.pass.cpp | 4 +- .../format/format.functions/format.pass.cpp | 4 +- .../format/format.functions/format_tests.h | 149 ++++----------------- .../format.functions/format_to.locale.pass.cpp | 4 +- .../format/format.functions/format_to.pass.cpp | 4 +- .../format.functions/format_to_n.locale.pass.cpp | 4 +- .../format/format.functions/format_to_n.pass.cpp | 4 +- .../formatted_size.locale.pass.cpp | 4 +- .../format.functions/formatted_size.pass.cpp | 4 +- .../format.functions/vformat.locale.pass.cpp | 4 +- .../format/format.functions/vformat.pass.cpp | 5 +- .../format.functions/vformat_to.locale.pass.cpp | 4 +- .../format/format.functions/vformat_to.pass.cpp | 4 +- libcxx/test/support/format.functions.common.h | 130 ++++++++++++++++++ 14 files changed, 179 insertions(+), 149 deletions(-) create mode 100644 libcxx/test/support/format.functions.common.h diff --git a/libcxx/test/std/utilities/format/format.functions/format.locale.pass.cpp b/libcxx/test/std/utilities/format/format.functions/format.locale.pass.cpp index a50c416..76d4b35 100644 --- a/libcxx/test/std/utilities/format/format.functions/format.locale.pass.cpp +++ b/libcxx/test/std/utilities/format/format.functions/format.locale.pass.cpp @@ -47,11 +47,11 @@ auto test_exception = [](std::string_view, std::basi }; int main(int, char**) { - format_tests(test, test_exception); + format_tests(test, test_exception); #ifndef TEST_HAS_NO_WIDE_CHARACTERS format_tests_char_to_wchar_t(test); - format_tests(test, test_exception); + format_tests(test, test_exception); #endif return 0; diff --git a/libcxx/test/std/utilities/format/format.functions/format.pass.cpp b/libcxx/test/std/utilities/format/format.functions/format.pass.cpp index 1bcc216a..2710078 100644 --- a/libcxx/test/std/utilities/format/format.functions/format.pass.cpp +++ b/libcxx/test/std/utilities/format/format.functions/format.pass.cpp @@ -56,11 +56,11 @@ auto test_exception = [](std::string_view, std::basi }; int main(int, char**) { - format_tests(test, test_exception); + format_tests(test, test_exception); #ifndef TEST_HAS_NO_WIDE_CHARACTERS format_tests_char_to_wchar_t(test); - format_tests(test, test_exception); + format_tests(test, test_exception); #endif return 0; diff --git a/libcxx/test/std/utilities/format/format.functions/format_tests.h b/libcxx/test/std/utilities/format/format.functions/format_tests.h index d5de3aa..86d11f2 100644 --- a/libcxx/test/std/utilities/format/format.functions/format_tests.h +++ b/libcxx/test/std/utilities/format/format.functions/format_tests.h @@ -17,126 +17,15 @@ #include #include -#include "make_string.h" #include "string_literal.h" #include "test_macros.h" +#include "format.functions.common.h" // In this file the following template types are used: // TestFunction must be callable as check(expected-result, string-to-format, args-to-format...) // ExceptionTest must be callable as check_exception(expected-exception, string-to-format, args-to-format...) -#define STR(S) MAKE_STRING(CharT, S) -#define SV(S) MAKE_STRING_VIEW(CharT, S) -#define CSTR(S) MAKE_CSTRING(CharT, S) - -template -struct context {}; - -template <> -struct context { - using type = std::format_context; -}; - -#ifndef TEST_HAS_NO_WIDE_CHARACTERS -template <> -struct context { - using type = std::wformat_context; -}; -#endif - -template -using context_t = typename context::type; - -// A user-defined type used to test the handle formatter. -enum class status : uint16_t { foo = 0xAAAA, bar = 0x5555, foobar = 0xAA55 }; - -// The formatter for a user-defined type used to test the handle formatter. -template -struct std::formatter { - int type = 0; - - constexpr auto parse(basic_format_parse_context& parse_ctx) -> decltype(parse_ctx.begin()) { - auto begin = parse_ctx.begin(); - auto end = parse_ctx.end(); - if (begin == end) - return begin; - - switch (*begin) { - case CharT('x'): - break; - case CharT('X'): - type = 1; - break; - case CharT('s'): - type = 2; - break; - case CharT('}'): - return begin; - default: - throw_format_error("The format-spec type has a type not supported for a status argument"); - } - - ++begin; - if (begin != end && *begin != CharT('}')) - throw_format_error("The format-spec should consume the input or end with a '}'"); - - return begin; - } - - template - auto format(status s, basic_format_context& ctx) const -> decltype(ctx.out()) { - const char* names[] = {"foo", "bar", "foobar"}; - char buffer[7]; - const char* begin = names[0]; - const char* end = names[0]; - switch (type) { - case 0: - begin = buffer; - buffer[0] = '0'; - buffer[1] = 'x'; - end = std::to_chars(&buffer[2], std::end(buffer), static_cast(s), 16).ptr; - buffer[6] = '\0'; - break; - - case 1: - begin = buffer; - buffer[0] = '0'; - buffer[1] = 'X'; - end = std::to_chars(&buffer[2], std::end(buffer), static_cast(s), 16).ptr; - std::transform(static_cast(&buffer[2]), end, &buffer[2], [](char c) { - return static_cast(std::toupper(c)); }); - buffer[6] = '\0'; - break; - - case 2: - switch (s) { - case status::foo: - begin = names[0]; - break; - case status::bar: - begin = names[1]; - break; - case status::foobar: - begin = names[2]; - break; - } - end = begin + strlen(begin); - break; - } - - return std::copy(begin, end, ctx.out()); - } - -private: - void throw_format_error(const char* s) { -#ifndef TEST_HAS_NO_EXCEPTIONS - throw std::format_error(s); -#else - (void)s; - std::abort(); -#endif - } -}; +enum class execution_modus { partial, full }; template std::vector> invalid_types(std::string_view valid) { @@ -2722,7 +2611,7 @@ void format_test_buffer_optimizations(TestFunction check) { check(std::basic_string_view{fill + str}, SV("{:*>{}}"), str, minimum + str.size()); } -template +template void format_tests(TestFunction check, ExceptionTest check_exception) { // *** Test escaping *** @@ -2797,8 +2686,10 @@ void format_tests(TestFunction check, ExceptionTest check_exception) { CharT('A'), CharT('Z'), CharT('!')); - format_test_char(check, check_exception); - format_test_char_as_integer(check, check_exception); + if constexpr (modus == execution_modus::full) { + format_test_char(check, check_exception); + format_test_char_as_integer(check, check_exception); + } // *** Test string format argument *** { @@ -2820,13 +2711,16 @@ void format_tests(TestFunction check, ExceptionTest check_exception) { std::basic_string_view data = buffer; check(SV("hello world"), SV("hello {}"), data); } - format_string_tests(check, check_exception); + if constexpr (modus == execution_modus::full) + format_string_tests(check, check_exception); // *** Test Boolean format argument *** check(SV("hello false true"), SV("hello {} {}"), false, true); - format_test_bool(check, check_exception); - format_test_bool_as_integer(check, check_exception); + if constexpr (modus == execution_modus::full) { + format_test_bool(check, check_exception); + format_test_bool_as_integer(check, check_exception); + } // *** Test signed integral format argument *** check(SV("hello 42"), SV("hello {}"), static_cast(42)); @@ -2837,7 +2731,8 @@ void format_tests(TestFunction check, ExceptionTest check_exception) { #ifndef TEST_HAS_NO_INT128 check(SV("hello 42"), SV("hello {}"), static_cast<__int128_t>(42)); #endif - format_test_signed_integer(check, check_exception); + if constexpr (modus == execution_modus::full) + format_test_signed_integer(check, check_exception); // ** Test unsigned integral format argument *** check(SV("hello 42"), SV("hello {}"), static_cast(42)); @@ -2848,25 +2743,29 @@ void format_tests(TestFunction check, ExceptionTest check_exception) { #ifndef TEST_HAS_NO_INT128 check(SV("hello 42"), SV("hello {}"), static_cast<__uint128_t>(42)); #endif - format_test_unsigned_integer(check, check_exception); + if constexpr (modus == execution_modus::full) + format_test_unsigned_integer(check, check_exception); // *** Test floating point format argument *** check(SV("hello 42"), SV("hello {}"), static_cast(42)); check(SV("hello 42"), SV("hello {}"), static_cast(42)); check(SV("hello 42"), SV("hello {}"), static_cast(42)); - format_test_floating_point(check, check_exception); + if constexpr (modus == execution_modus::full) + format_test_floating_point(check, check_exception); // *** Test pointer formater argument *** check(SV("hello 0x0"), SV("hello {}"), nullptr); check(SV("hello 0x42"), SV("hello {}"), reinterpret_cast(0x42)); check(SV("hello 0x42"), SV("hello {}"), reinterpret_cast(0x42)); - format_test_pointer(check, check_exception); + if constexpr (modus == execution_modus::full) + format_test_pointer(check, check_exception); // *** Test handle formatter argument *** format_test_handle(check, check_exception); // *** Test the interal buffer optimizations *** - format_test_buffer_optimizations(check); + if constexpr (modus == execution_modus::full) + format_test_buffer_optimizations(check); } #ifndef TEST_HAS_NO_WIDE_CHARACTERS @@ -2877,4 +2776,4 @@ void format_tests_char_to_wchar_t(TestFunction check) { } #endif -#endif +#endif // TEST_STD_UTILITIES_FORMAT_FORMAT_FUNCTIONS_FORMAT_TESTS_H diff --git a/libcxx/test/std/utilities/format/format.functions/format_to.locale.pass.cpp b/libcxx/test/std/utilities/format/format.functions/format_to.locale.pass.cpp index 1bd6dc8..f497e2d 100644 --- a/libcxx/test/std/utilities/format/format.functions/format_to.locale.pass.cpp +++ b/libcxx/test/std/utilities/format/format.functions/format_to.locale.pass.cpp @@ -70,11 +70,11 @@ auto test_exception = [](std::string_view, std::basi }; int main(int, char**) { - format_tests(test, test_exception); + format_tests(test, test_exception); #ifndef TEST_HAS_NO_WIDE_CHARACTERS format_tests_char_to_wchar_t(test); - format_tests(test, test_exception); + format_tests(test, test_exception); #endif return 0; diff --git a/libcxx/test/std/utilities/format/format.functions/format_to.pass.cpp b/libcxx/test/std/utilities/format/format.functions/format_to.pass.cpp index 16d2878..90615f7 100644 --- a/libcxx/test/std/utilities/format/format.functions/format_to.pass.cpp +++ b/libcxx/test/std/utilities/format/format.functions/format_to.pass.cpp @@ -66,11 +66,11 @@ auto test_exception = [](std::string_view, std::basi }; int main(int, char**) { - format_tests(test, test_exception); + format_tests(test, test_exception); #ifndef TEST_HAS_NO_WIDE_CHARACTERS format_tests_char_to_wchar_t(test); - format_tests(test, test_exception); + format_tests(test, test_exception); #endif return 0; diff --git a/libcxx/test/std/utilities/format/format.functions/format_to_n.locale.pass.cpp b/libcxx/test/std/utilities/format/format.functions/format_to_n.locale.pass.cpp index ee16552..8a9a539 100644 --- a/libcxx/test/std/utilities/format/format.functions/format_to_n.locale.pass.cpp +++ b/libcxx/test/std/utilities/format/format.functions/format_to_n.locale.pass.cpp @@ -106,11 +106,11 @@ auto test_exception = [](std::string_view, std::basi }; int main(int, char**) { - format_tests(test, test_exception); + format_tests(test, test_exception); #ifndef TEST_HAS_NO_WIDE_CHARACTERS format_tests_char_to_wchar_t(test); - format_tests(test, test_exception); + format_tests(test, test_exception); #endif return 0; diff --git a/libcxx/test/std/utilities/format/format.functions/format_to_n.pass.cpp b/libcxx/test/std/utilities/format/format.functions/format_to_n.pass.cpp index e758e65..8ce91da 100644 --- a/libcxx/test/std/utilities/format/format.functions/format_to_n.pass.cpp +++ b/libcxx/test/std/utilities/format/format.functions/format_to_n.pass.cpp @@ -100,11 +100,11 @@ auto test_exception = [](std::string_view, std::basi }; int main(int, char**) { - format_tests(test, test_exception); + format_tests(test, test_exception); #ifndef TEST_HAS_NO_WIDE_CHARACTERS format_tests_char_to_wchar_t(test); - format_tests(test, test_exception); + format_tests(test, test_exception); #endif return 0; diff --git a/libcxx/test/std/utilities/format/format.functions/formatted_size.locale.pass.cpp b/libcxx/test/std/utilities/format/format.functions/formatted_size.locale.pass.cpp index 1317f7f..625b99f 100644 --- a/libcxx/test/std/utilities/format/format.functions/formatted_size.locale.pass.cpp +++ b/libcxx/test/std/utilities/format/format.functions/formatted_size.locale.pass.cpp @@ -44,11 +44,11 @@ auto test_exception = [](std::string_view, std::basi }; int main(int, char**) { - format_tests(test, test_exception); + format_tests(test, test_exception); #ifndef TEST_HAS_NO_WIDE_CHARACTERS format_tests_char_to_wchar_t(test); - format_tests(test, test_exception); + format_tests(test, test_exception); #endif return 0; diff --git a/libcxx/test/std/utilities/format/format.functions/formatted_size.pass.cpp b/libcxx/test/std/utilities/format/format.functions/formatted_size.pass.cpp index 8f0eae0..6a627ec 100644 --- a/libcxx/test/std/utilities/format/format.functions/formatted_size.pass.cpp +++ b/libcxx/test/std/utilities/format/format.functions/formatted_size.pass.cpp @@ -41,11 +41,11 @@ auto test_exception = [](std::string_view, std::basi }; int main(int, char**) { - format_tests(test, test_exception); + format_tests(test, test_exception); #ifndef TEST_HAS_NO_WIDE_CHARACTERS format_tests_char_to_wchar_t(test); - format_tests(test, test_exception); + format_tests(test, test_exception); #endif return 0; diff --git a/libcxx/test/std/utilities/format/format.functions/vformat.locale.pass.cpp b/libcxx/test/std/utilities/format/format.functions/vformat.locale.pass.cpp index c2d53de..11bf67f0 100644 --- a/libcxx/test/std/utilities/format/format.functions/vformat.locale.pass.cpp +++ b/libcxx/test/std/utilities/format/format.functions/vformat.locale.pass.cpp @@ -48,11 +48,11 @@ auto test_exception = }; int main(int, char**) { - format_tests(test, test_exception); + format_tests(test, test_exception); #ifndef TEST_HAS_NO_WIDE_CHARACTERS format_tests_char_to_wchar_t(test); - format_tests(test, test_exception); + format_tests(test, test_exception); #endif return 0; diff --git a/libcxx/test/std/utilities/format/format.functions/vformat.pass.cpp b/libcxx/test/std/utilities/format/format.functions/vformat.pass.cpp index 7692e09..d115615 100644 --- a/libcxx/test/std/utilities/format/format.functions/vformat.pass.cpp +++ b/libcxx/test/std/utilities/format/format.functions/vformat.pass.cpp @@ -22,6 +22,7 @@ #include "test_macros.h" #include "format_tests.h" #include "string_literal.h" + auto test = []( std::basic_string_view expected, std::basic_string_view fmt, Args&&... args) constexpr { std::basic_string out = std::vformat(fmt, std::make_format_args>(args...)); @@ -46,11 +47,11 @@ auto test_exception = }; int main(int, char**) { - format_tests(test, test_exception); + format_tests(test, test_exception); #ifndef TEST_HAS_NO_WIDE_CHARACTERS format_tests_char_to_wchar_t(test); - format_tests(test, test_exception); + format_tests(test, test_exception); #endif return 0; diff --git a/libcxx/test/std/utilities/format/format.functions/vformat_to.locale.pass.cpp b/libcxx/test/std/utilities/format/format.functions/vformat_to.locale.pass.cpp index 3d1f418..993f19a 100644 --- a/libcxx/test/std/utilities/format/format.functions/vformat_to.locale.pass.cpp +++ b/libcxx/test/std/utilities/format/format.functions/vformat_to.locale.pass.cpp @@ -77,11 +77,11 @@ auto test_exception = }; int main(int, char**) { - format_tests(test, test_exception); + format_tests(test, test_exception); #ifndef TEST_HAS_NO_WIDE_CHARACTERS format_tests_char_to_wchar_t(test); - format_tests(test, test_exception); + format_tests(test, test_exception); #endif return 0; diff --git a/libcxx/test/std/utilities/format/format.functions/vformat_to.pass.cpp b/libcxx/test/std/utilities/format/format.functions/vformat_to.pass.cpp index 8a8bf6f..0574d49 100644 --- a/libcxx/test/std/utilities/format/format.functions/vformat_to.pass.cpp +++ b/libcxx/test/std/utilities/format/format.functions/vformat_to.pass.cpp @@ -74,11 +74,11 @@ auto test_exception = }; int main(int, char**) { - format_tests(test, test_exception); + format_tests(test, test_exception); #ifndef TEST_HAS_NO_WIDE_CHARACTERS format_tests_char_to_wchar_t(test); - format_tests(test, test_exception); + format_tests(test, test_exception); #endif return 0; diff --git a/libcxx/test/support/format.functions.common.h b/libcxx/test/support/format.functions.common.h new file mode 100644 index 0000000..fa107ba --- /dev/null +++ b/libcxx/test/support/format.functions.common.h @@ -0,0 +1,130 @@ +//===----------------------------------------------------------------------===// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef TEST_SUPPORT_FORMAT_FUNCTIONS_COMMON_H +#define TEST_SUPPORT_FORMAT_FUNCTIONS_COMMON_H + +// Contains the common part of the formatter tests for different papers. + +#include + +#include "make_string.h" + +#define STR(S) MAKE_STRING(CharT, S) +#define SV(S) MAKE_STRING_VIEW(CharT, S) +#define CSTR(S) MAKE_CSTRING(CharT, S) + +template +struct context {}; + +template <> +struct context { + using type = std::format_context; +}; + +#ifndef TEST_HAS_NO_WIDE_CHARACTERS +template <> +struct context { + using type = std::wformat_context; +}; +#endif + +template +using context_t = typename context::type; + +// A user-defined type used to test the handle formatter. +enum class status : uint16_t { foo = 0xAAAA, bar = 0x5555, foobar = 0xAA55 }; + +// The formatter for a user-defined type used to test the handle formatter. +template +struct std::formatter { + int type = 0; + + constexpr auto parse(basic_format_parse_context& parse_ctx) -> decltype(parse_ctx.begin()) { + auto begin = parse_ctx.begin(); + auto end = parse_ctx.end(); + if (begin == end) + return begin; + + switch (*begin) { + case CharT('x'): + break; + case CharT('X'): + type = 1; + break; + case CharT('s'): + type = 2; + break; + case CharT('}'): + return begin; + default: + throw_format_error("The format-spec type has a type not supported for a status argument"); + } + + ++begin; + if (begin != end && *begin != CharT('}')) + throw_format_error("The format-spec should consume the input or end with a '}'"); + + return begin; + } + + template + auto format(status s, basic_format_context& ctx) const -> decltype(ctx.out()) { + const char* names[] = {"foo", "bar", "foobar"}; + char buffer[7]; + const char* begin = names[0]; + const char* end = names[0]; + switch (type) { + case 0: + begin = buffer; + buffer[0] = '0'; + buffer[1] = 'x'; + end = std::to_chars(&buffer[2], std::end(buffer), static_cast(s), 16).ptr; + buffer[6] = '\0'; + break; + + case 1: + begin = buffer; + buffer[0] = '0'; + buffer[1] = 'X'; + end = std::to_chars(&buffer[2], std::end(buffer), static_cast(s), 16).ptr; + std::transform(static_cast(&buffer[2]), end, &buffer[2], [](char c) { + return static_cast(std::toupper(c)); }); + buffer[6] = '\0'; + break; + + case 2: + switch (s) { + case status::foo: + begin = names[0]; + break; + case status::bar: + begin = names[1]; + break; + case status::foobar: + begin = names[2]; + break; + } + end = begin + strlen(begin); + break; + } + + return std::copy(begin, end, ctx.out()); + } + +private: + void throw_format_error(const char* s) { +#ifndef TEST_HAS_NO_EXCEPTIONS + throw std::format_error(s); +#else + (void)s; + std::abort(); +#endif + } +}; + +#endif // TEST_SUPPORT_FORMAT_FUNCTIONS_COMMON_H -- 2.7.4