[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
GitOrigin-RevId: 49d4fee9940f5e1273cc0ae30da82df9c1437706
diff --git a/test/std/utilities/format/format.functions/format.locale.pass.cpp b/test/std/utilities/format/format.functions/format.locale.pass.cpp
index a50c416..76d4b35 100644
--- a/test/std/utilities/format/format.functions/format.locale.pass.cpp
+++ b/test/std/utilities/format/format.functions/format.locale.pass.cpp
@@ -47,11 +47,11 @@
};
int main(int, char**) {
- format_tests<char>(test, test_exception);
+ format_tests<char, execution_modus::full>(test, test_exception);
#ifndef TEST_HAS_NO_WIDE_CHARACTERS
format_tests_char_to_wchar_t(test);
- format_tests<wchar_t>(test, test_exception);
+ format_tests<wchar_t, execution_modus::full>(test, test_exception);
#endif
return 0;
diff --git a/test/std/utilities/format/format.functions/format.pass.cpp b/test/std/utilities/format/format.functions/format.pass.cpp
index 1bcc216..2710078 100644
--- a/test/std/utilities/format/format.functions/format.pass.cpp
+++ b/test/std/utilities/format/format.functions/format.pass.cpp
@@ -56,11 +56,11 @@
};
int main(int, char**) {
- format_tests<char>(test, test_exception);
+ format_tests<char, execution_modus::full>(test, test_exception);
#ifndef TEST_HAS_NO_WIDE_CHARACTERS
format_tests_char_to_wchar_t(test);
- format_tests<wchar_t>(test, test_exception);
+ format_tests<wchar_t, execution_modus::full>(test, test_exception);
#endif
return 0;
diff --git a/test/std/utilities/format/format.functions/format_tests.h b/test/std/utilities/format/format.functions/format_tests.h
index d5de3aa..86d11f2 100644
--- a/test/std/utilities/format/format.functions/format_tests.h
+++ b/test/std/utilities/format/format.functions/format_tests.h
@@ -17,126 +17,15 @@
#include <cstdint>
#include <iterator>
-#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 <class T>
-struct context {};
-
-template <>
-struct context<char> {
- using type = std::format_context;
-};
-
-#ifndef TEST_HAS_NO_WIDE_CHARACTERS
-template <>
-struct context<wchar_t> {
- using type = std::wformat_context;
-};
-#endif
-
-template <class T>
-using context_t = typename context<T>::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 <class CharT>
-struct std::formatter<status, CharT> {
- int type = 0;
-
- constexpr auto parse(basic_format_parse_context<CharT>& 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 <class Out>
- auto format(status s, basic_format_context<Out, CharT>& 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<uint16_t>(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<uint16_t>(s), 16).ptr;
- std::transform(static_cast<const char*>(&buffer[2]), end, &buffer[2], [](char c) {
- return static_cast<char>(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 <class CharT>
std::vector<std::basic_string_view<CharT>> invalid_types(std::string_view valid) {
@@ -2722,7 +2611,7 @@
check(std::basic_string_view<CharT>{fill + str}, SV("{:*>{}}"), str, minimum + str.size());
}
-template <class CharT, class TestFunction, class ExceptionTest>
+template <class CharT, execution_modus modus, class TestFunction, class ExceptionTest>
void format_tests(TestFunction check, ExceptionTest check_exception) {
// *** Test escaping ***
@@ -2797,8 +2686,10 @@
CharT('A'),
CharT('Z'),
CharT('!'));
- format_test_char<CharT>(check, check_exception);
- format_test_char_as_integer<CharT>(check, check_exception);
+ if constexpr (modus == execution_modus::full) {
+ format_test_char<CharT>(check, check_exception);
+ format_test_char_as_integer<CharT>(check, check_exception);
+ }
// *** Test string format argument ***
{
@@ -2820,13 +2711,16 @@
std::basic_string_view<CharT> data = buffer;
check(SV("hello world"), SV("hello {}"), data);
}
- format_string_tests<CharT>(check, check_exception);
+ if constexpr (modus == execution_modus::full)
+ format_string_tests<CharT>(check, check_exception);
// *** Test Boolean format argument ***
check(SV("hello false true"), SV("hello {} {}"), false, true);
- format_test_bool<CharT>(check, check_exception);
- format_test_bool_as_integer<CharT>(check, check_exception);
+ if constexpr (modus == execution_modus::full) {
+ format_test_bool<CharT>(check, check_exception);
+ format_test_bool_as_integer<CharT>(check, check_exception);
+ }
// *** Test signed integral format argument ***
check(SV("hello 42"), SV("hello {}"), static_cast<signed char>(42));
@@ -2837,7 +2731,8 @@
#ifndef TEST_HAS_NO_INT128
check(SV("hello 42"), SV("hello {}"), static_cast<__int128_t>(42));
#endif
- format_test_signed_integer<CharT>(check, check_exception);
+ if constexpr (modus == execution_modus::full)
+ format_test_signed_integer<CharT>(check, check_exception);
// ** Test unsigned integral format argument ***
check(SV("hello 42"), SV("hello {}"), static_cast<unsigned char>(42));
@@ -2848,25 +2743,29 @@
#ifndef TEST_HAS_NO_INT128
check(SV("hello 42"), SV("hello {}"), static_cast<__uint128_t>(42));
#endif
- format_test_unsigned_integer<CharT>(check, check_exception);
+ if constexpr (modus == execution_modus::full)
+ format_test_unsigned_integer<CharT>(check, check_exception);
// *** Test floating point format argument ***
check(SV("hello 42"), SV("hello {}"), static_cast<float>(42));
check(SV("hello 42"), SV("hello {}"), static_cast<double>(42));
check(SV("hello 42"), SV("hello {}"), static_cast<long double>(42));
- format_test_floating_point<CharT>(check, check_exception);
+ if constexpr (modus == execution_modus::full)
+ format_test_floating_point<CharT>(check, check_exception);
// *** Test pointer formater argument ***
check(SV("hello 0x0"), SV("hello {}"), nullptr);
check(SV("hello 0x42"), SV("hello {}"), reinterpret_cast<void*>(0x42));
check(SV("hello 0x42"), SV("hello {}"), reinterpret_cast<const void*>(0x42));
- format_test_pointer<CharT>(check, check_exception);
+ if constexpr (modus == execution_modus::full)
+ format_test_pointer<CharT>(check, check_exception);
// *** Test handle formatter argument ***
format_test_handle<CharT>(check, check_exception);
// *** Test the interal buffer optimizations ***
- format_test_buffer_optimizations<CharT>(check);
+ if constexpr (modus == execution_modus::full)
+ format_test_buffer_optimizations<CharT>(check);
}
#ifndef TEST_HAS_NO_WIDE_CHARACTERS
@@ -2877,4 +2776,4 @@
}
#endif
-#endif
+#endif // TEST_STD_UTILITIES_FORMAT_FORMAT_FUNCTIONS_FORMAT_TESTS_H
diff --git a/test/std/utilities/format/format.functions/format_to.locale.pass.cpp b/test/std/utilities/format/format.functions/format_to.locale.pass.cpp
index 1bd6dc8..f497e2d 100644
--- a/test/std/utilities/format/format.functions/format_to.locale.pass.cpp
+++ b/test/std/utilities/format/format.functions/format_to.locale.pass.cpp
@@ -70,11 +70,11 @@
};
int main(int, char**) {
- format_tests<char>(test, test_exception);
+ format_tests<char, execution_modus::partial>(test, test_exception);
#ifndef TEST_HAS_NO_WIDE_CHARACTERS
format_tests_char_to_wchar_t(test);
- format_tests<wchar_t>(test, test_exception);
+ format_tests<wchar_t, execution_modus::partial>(test, test_exception);
#endif
return 0;
diff --git a/test/std/utilities/format/format.functions/format_to.pass.cpp b/test/std/utilities/format/format.functions/format_to.pass.cpp
index 16d2878..90615f7 100644
--- a/test/std/utilities/format/format.functions/format_to.pass.cpp
+++ b/test/std/utilities/format/format.functions/format_to.pass.cpp
@@ -66,11 +66,11 @@
};
int main(int, char**) {
- format_tests<char>(test, test_exception);
+ format_tests<char, execution_modus::partial>(test, test_exception);
#ifndef TEST_HAS_NO_WIDE_CHARACTERS
format_tests_char_to_wchar_t(test);
- format_tests<wchar_t>(test, test_exception);
+ format_tests<wchar_t, execution_modus::partial>(test, test_exception);
#endif
return 0;
diff --git a/test/std/utilities/format/format.functions/format_to_n.locale.pass.cpp b/test/std/utilities/format/format.functions/format_to_n.locale.pass.cpp
index ee16552..8a9a539 100644
--- a/test/std/utilities/format/format.functions/format_to_n.locale.pass.cpp
+++ b/test/std/utilities/format/format.functions/format_to_n.locale.pass.cpp
@@ -106,11 +106,11 @@
};
int main(int, char**) {
- format_tests<char>(test, test_exception);
+ format_tests<char, execution_modus::partial>(test, test_exception);
#ifndef TEST_HAS_NO_WIDE_CHARACTERS
format_tests_char_to_wchar_t(test);
- format_tests<wchar_t>(test, test_exception);
+ format_tests<wchar_t, execution_modus::partial>(test, test_exception);
#endif
return 0;
diff --git a/test/std/utilities/format/format.functions/format_to_n.pass.cpp b/test/std/utilities/format/format.functions/format_to_n.pass.cpp
index e758e65..8ce91da 100644
--- a/test/std/utilities/format/format.functions/format_to_n.pass.cpp
+++ b/test/std/utilities/format/format.functions/format_to_n.pass.cpp
@@ -100,11 +100,11 @@
};
int main(int, char**) {
- format_tests<char>(test, test_exception);
+ format_tests<char, execution_modus::partial>(test, test_exception);
#ifndef TEST_HAS_NO_WIDE_CHARACTERS
format_tests_char_to_wchar_t(test);
- format_tests<wchar_t>(test, test_exception);
+ format_tests<wchar_t, execution_modus::partial>(test, test_exception);
#endif
return 0;
diff --git a/test/std/utilities/format/format.functions/formatted_size.locale.pass.cpp b/test/std/utilities/format/format.functions/formatted_size.locale.pass.cpp
index 1317f7f..625b99f 100644
--- a/test/std/utilities/format/format.functions/formatted_size.locale.pass.cpp
+++ b/test/std/utilities/format/format.functions/formatted_size.locale.pass.cpp
@@ -44,11 +44,11 @@
};
int main(int, char**) {
- format_tests<char>(test, test_exception);
+ format_tests<char, execution_modus::partial>(test, test_exception);
#ifndef TEST_HAS_NO_WIDE_CHARACTERS
format_tests_char_to_wchar_t(test);
- format_tests<wchar_t>(test, test_exception);
+ format_tests<wchar_t, execution_modus::partial>(test, test_exception);
#endif
return 0;
diff --git a/test/std/utilities/format/format.functions/formatted_size.pass.cpp b/test/std/utilities/format/format.functions/formatted_size.pass.cpp
index 8f0eae0..6a627ec 100644
--- a/test/std/utilities/format/format.functions/formatted_size.pass.cpp
+++ b/test/std/utilities/format/format.functions/formatted_size.pass.cpp
@@ -41,11 +41,11 @@
};
int main(int, char**) {
- format_tests<char>(test, test_exception);
+ format_tests<char, execution_modus::partial>(test, test_exception);
#ifndef TEST_HAS_NO_WIDE_CHARACTERS
format_tests_char_to_wchar_t(test);
- format_tests<wchar_t>(test, test_exception);
+ format_tests<wchar_t, execution_modus::partial>(test, test_exception);
#endif
return 0;
diff --git a/test/std/utilities/format/format.functions/vformat.locale.pass.cpp b/test/std/utilities/format/format.functions/vformat.locale.pass.cpp
index c2d53de..11bf67f 100644
--- a/test/std/utilities/format/format.functions/vformat.locale.pass.cpp
+++ b/test/std/utilities/format/format.functions/vformat.locale.pass.cpp
@@ -48,11 +48,11 @@
};
int main(int, char**) {
- format_tests<char>(test, test_exception);
+ format_tests<char, execution_modus::full>(test, test_exception);
#ifndef TEST_HAS_NO_WIDE_CHARACTERS
format_tests_char_to_wchar_t(test);
- format_tests<wchar_t>(test, test_exception);
+ format_tests<wchar_t, execution_modus::full>(test, test_exception);
#endif
return 0;
diff --git a/test/std/utilities/format/format.functions/vformat.pass.cpp b/test/std/utilities/format/format.functions/vformat.pass.cpp
index 7692e09..d115615 100644
--- a/test/std/utilities/format/format.functions/vformat.pass.cpp
+++ b/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 = []<class CharT, class... Args>(
std::basic_string_view<CharT> expected, std::basic_string_view<CharT> fmt, Args&&... args) constexpr {
std::basic_string<CharT> out = std::vformat(fmt, std::make_format_args<context_t<CharT>>(args...));
@@ -46,11 +47,11 @@
};
int main(int, char**) {
- format_tests<char>(test, test_exception);
+ format_tests<char, execution_modus::full>(test, test_exception);
#ifndef TEST_HAS_NO_WIDE_CHARACTERS
format_tests_char_to_wchar_t(test);
- format_tests<wchar_t>(test, test_exception);
+ format_tests<wchar_t, execution_modus::full>(test, test_exception);
#endif
return 0;
diff --git a/test/std/utilities/format/format.functions/vformat_to.locale.pass.cpp b/test/std/utilities/format/format.functions/vformat_to.locale.pass.cpp
index 3d1f418..993f19a 100644
--- a/test/std/utilities/format/format.functions/vformat_to.locale.pass.cpp
+++ b/test/std/utilities/format/format.functions/vformat_to.locale.pass.cpp
@@ -77,11 +77,11 @@
};
int main(int, char**) {
- format_tests<char>(test, test_exception);
+ format_tests<char, execution_modus::partial>(test, test_exception);
#ifndef TEST_HAS_NO_WIDE_CHARACTERS
format_tests_char_to_wchar_t(test);
- format_tests<wchar_t>(test, test_exception);
+ format_tests<wchar_t, execution_modus::partial>(test, test_exception);
#endif
return 0;
diff --git a/test/std/utilities/format/format.functions/vformat_to.pass.cpp b/test/std/utilities/format/format.functions/vformat_to.pass.cpp
index 8a8bf6f..0574d49 100644
--- a/test/std/utilities/format/format.functions/vformat_to.pass.cpp
+++ b/test/std/utilities/format/format.functions/vformat_to.pass.cpp
@@ -74,11 +74,11 @@
};
int main(int, char**) {
- format_tests<char>(test, test_exception);
+ format_tests<char, execution_modus::partial>(test, test_exception);
#ifndef TEST_HAS_NO_WIDE_CHARACTERS
format_tests_char_to_wchar_t(test);
- format_tests<wchar_t>(test, test_exception);
+ format_tests<wchar_t, execution_modus::partial>(test, test_exception);
#endif
return 0;
diff --git a/test/support/format.functions.common.h b/test/support/format.functions.common.h
new file mode 100644
index 0000000..fa107ba
--- /dev/null
+++ b/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 <format>
+
+#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 <class T>
+struct context {};
+
+template <>
+struct context<char> {
+ using type = std::format_context;
+};
+
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+template <>
+struct context<wchar_t> {
+ using type = std::wformat_context;
+};
+#endif
+
+template <class T>
+using context_t = typename context<T>::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 <class CharT>
+struct std::formatter<status, CharT> {
+ int type = 0;
+
+ constexpr auto parse(basic_format_parse_context<CharT>& 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 <class Out>
+ auto format(status s, basic_format_context<Out, CharT>& 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<uint16_t>(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<uint16_t>(s), 16).ptr;
+ std::transform(static_cast<const char*>(&buffer[2]), end, &buffer[2], [](char c) {
+ return static_cast<char>(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