diff --git a/libcxx/test/std/utilities/format/format.functions/escaped_output.ascii.pass.cpp b/libcxx/test/std/utilities/format/format.functions/escaped_output.ascii.pass.cpp --- a/libcxx/test/std/utilities/format/format.functions/escaped_output.ascii.pass.cpp +++ b/libcxx/test/std/utilities/format/format.functions/escaped_output.ascii.pass.cpp @@ -24,6 +24,7 @@ #include "test_macros.h" #include "make_string.h" #include "test_format_string.h" +#include "test_validation.h" #ifndef TEST_HAS_NO_LOCALIZATION # include @@ -35,19 +36,14 @@ std::basic_string_view expected, test_format_string fmt, Args&&... args) { { std::basic_string out = std::format(fmt, std::forward(args)...); -#ifndef TEST_HAS_NO_LOCALIZATION - if (out != expected) { - if constexpr (std::same_as) - std::cerr << "\nFormat string " << fmt.get() << "\nExpected output " << expected << "\nActual output " - << out << '\n'; -# ifndef TEST_HAS_NO_WIDE_CHARACTERS - else - std::wcerr << L"\nFormat string " << fmt.get() << L"\nExpected output " << expected << L"\nActual output " - << out << L'\n'; -# endif // TEST_HAS_NO_WIDE_CHARACTERS - } -#endif // TEST_HAS_NO_LOCALIZATION - assert(out == expected); + REQUIRE(out == expected, + "\nFormat string ", + fmt.get(), + "\nExpected output ", + expected, + "\nActual output ", + out, + '\n'); } #ifndef TEST_HAS_NO_LOCALIZATION { diff --git a/libcxx/test/std/utilities/format/format.functions/escaped_output.unicode.pass.cpp b/libcxx/test/std/utilities/format/format.functions/escaped_output.unicode.pass.cpp --- a/libcxx/test/std/utilities/format/format.functions/escaped_output.unicode.pass.cpp +++ b/libcxx/test/std/utilities/format/format.functions/escaped_output.unicode.pass.cpp @@ -30,6 +30,7 @@ #include "test_macros.h" #include "make_string.h" #include "test_format_string.h" +#include "test_validation.h" #ifndef TEST_HAS_NO_LOCALIZATION # include @@ -41,19 +42,14 @@ std::basic_string_view expected, test_format_string fmt, Args&&... args) { { std::basic_string out = std::format(fmt, std::forward(args)...); -#ifndef TEST_HAS_NO_LOCALIZATION - if (out != expected) { - if constexpr (std::same_as) - std::cerr << "\nFormat string " << fmt.get() << "\nExpected output " << expected << "\nActual output " - << out << '\n'; -# ifndef TEST_HAS_NO_WIDE_CHARACTERS - else - std::wcerr << L"\nFormat string " << fmt.get() << L"\nExpected output " << expected << L"\nActual output " - << out << L'\n'; -# endif // TEST_HAS_NO_WIDE_CHARACTERS - } -#endif // TEST_HAS_NO_LOCALIZATION - assert(out == expected); + REQUIRE(out == expected, + "\nFormat string ", + fmt.get(), + "\nExpected output ", + expected, + "\nActual output ", + out, + '\n'); } #ifndef TEST_HAS_NO_LOCALIZATION { 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 --- a/libcxx/test/std/utilities/format/format.functions/format.locale.pass.cpp +++ b/libcxx/test/std/utilities/format/format.functions/format.locale.pass.cpp @@ -27,16 +27,20 @@ #include "format_tests.h" #include "string_literal.h" #include "test_format_string.h" +#include "test_validation.h" auto test = []( std::basic_string_view expected, test_format_string fmt, Args&&... args) constexpr { std::basic_string out = std::format(std::locale(), fmt, std::forward(args)...); - if constexpr (std::same_as) - if (out != expected) - std::cerr << "\nFormat string " << fmt.get() << "\nExpected output " << expected << "\nActual output " - << out << '\n'; - assert(out == expected); + REQUIRE(out == expected, + "\nFormat string ", + fmt.get(), + "\nExpected output ", + expected, + "\nActual output ", + out, + '\n'); }; auto test_exception = [](std::string_view, std::basic_string_view, Args&&...) { diff --git a/libcxx/test/std/utilities/format/format.functions/format.pass.cpp b/libcxx/test/std/utilities/format/format.functions/format.pass.cpp --- a/libcxx/test/std/utilities/format/format.functions/format.pass.cpp +++ b/libcxx/test/std/utilities/format/format.functions/format.pass.cpp @@ -29,23 +29,20 @@ #include "format_tests.h" #include "string_literal.h" #include "test_format_string.h" - -#ifndef TEST_HAS_NO_LOCALIZATION -# include -# include -#endif +#include "test_validation.h" auto test = []( std::basic_string_view expected, test_format_string fmt, Args&&... args) constexpr { std::basic_string out = std::format(fmt, std::forward(args)...); -#ifndef TEST_HAS_NO_LOCALIZATION - if constexpr (std::same_as) - if (out != expected) - std::cerr << "\nFormat string " << fmt.get() << "\nExpected output " << expected << "\nActual output " - << out << '\n'; -#endif - assert(out == expected); + REQUIRE(out == expected, + "\nFormat string ", + fmt.get(), + "\nExpected output ", + expected, + "\nActual output ", + out, + '\n'); }; auto test_exception = [](std::string_view, std::basic_string_view, Args&&...) { diff --git a/libcxx/test/std/utilities/format/format.functions/locale-specific_form.pass.cpp b/libcxx/test/std/utilities/format/format.functions/locale-specific_form.pass.cpp --- a/libcxx/test/std/utilities/format/format.functions/locale-specific_form.pass.cpp +++ b/libcxx/test/std/utilities/format/format.functions/locale-specific_form.pass.cpp @@ -93,6 +93,7 @@ #include "format_tests.h" #include "string_literal.h" #include "test_format_string.h" +#include "test_validation.h" #define STR(S) MAKE_STRING(CharT, S) #define SV(S) MAKE_STRING_VIEW(CharT, S) @@ -127,11 +128,14 @@ // *** format *** { std::basic_string out = std::format(fmt, std::forward(args)...); - if constexpr (std::same_as) - if (out != expected) - std::cerr << "\nFormat string " << fmt.get() << "\nExpected output " << expected << "\nActual output " - << out << '\n'; - assert(out == expected); + REQUIRE(out == expected, + "\nFormat string ", + fmt.get(), + "\nExpected output ", + expected, + "\nActual output ", + out, + '\n'); } // *** vformat *** { 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 --- a/libcxx/test/std/utilities/format/format.functions/vformat.locale.pass.cpp +++ b/libcxx/test/std/utilities/format/format.functions/vformat.locale.pass.cpp @@ -23,11 +23,12 @@ #include "test_macros.h" #include "format_tests.h" #include "string_literal.h" +#include "test_validation.h" auto test = []( std::basic_string_view expected, std::basic_string_view fmt, Args&&... args) constexpr { std::basic_string out = std::vformat(std::locale(), fmt, std::make_format_args>(args...)); - assert(out == expected); + REQUIRE(out == expected, "\nFormat string ", fmt, "\nExpected output ", expected, "\nActual output ", out, '\n'); }; auto test_exception = @@ -37,10 +38,18 @@ [[maybe_unused]] Args&&... args) { #ifndef TEST_HAS_NO_EXCEPTIONS try { - (void)std::vformat(std::locale(), fmt, std::make_format_args>(args...)); - assert(false); + TEST_IGNORE_NODISCARD std::vformat(std::locale(), fmt, std::make_format_args>(args...)); + FAIL("\nFormat string ", fmt, "\nDidn't throw an exception.\n"); } catch ([[maybe_unused]] const std::format_error& e) { - LIBCPP_ASSERT(e.what() == what); + LIBCPP_REQUIRE( + e.what() == what, + "\nFormat string ", + fmt, + "\nExpected exception ", + what, + "\nActual exception ", + e.what(), + '\n'); return; } assert(false); diff --git a/libcxx/test/std/utilities/format/format.functions/vformat.pass.cpp b/libcxx/test/std/utilities/format/format.functions/vformat.pass.cpp --- a/libcxx/test/std/utilities/format/format.functions/vformat.pass.cpp +++ b/libcxx/test/std/utilities/format/format.functions/vformat.pass.cpp @@ -22,11 +22,12 @@ #include "test_macros.h" #include "format_tests.h" #include "string_literal.h" +#include "test_validation.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...)); - assert(out == expected); + REQUIRE(out == expected, "\nFormat string ", fmt, "\nExpected output ", expected, "\nActual output ", out, '\n'); }; auto test_exception = @@ -37,9 +38,18 @@ #ifndef TEST_HAS_NO_EXCEPTIONS try { TEST_IGNORE_NODISCARD std::vformat(fmt, std::make_format_args>(args...)); - assert(false); + FAIL("\nFormat string ", fmt, "\nDidn't throw an exception.\n"); } catch ([[maybe_unused]] const std::format_error& e) { - LIBCPP_ASSERT(e.what() == what); + LIBCPP_REQUIRE( + e.what() == what, + "\nFormat string ", + fmt, + "\nExpected exception ", + what, + "\nActual exception ", + e.what(), + '\n'); + return; } assert(false); diff --git a/libcxx/test/std/utilities/format/format.tuple/format.functions.format.pass.cpp b/libcxx/test/std/utilities/format/format.tuple/format.functions.format.pass.cpp --- a/libcxx/test/std/utilities/format/format.tuple/format.functions.format.pass.cpp +++ b/libcxx/test/std/utilities/format/format.tuple/format.functions.format.pass.cpp @@ -30,22 +30,12 @@ #include "format.functions.tests.h" #include "test_format_string.h" #include "test_macros.h" - -#ifndef TEST_HAS_NO_LOCALIZATION -# include -# include -#endif +#include "test_validation.h" auto test = []( std::basic_string_view expected, test_format_string fmt, Args&&... args) { std::basic_string out = std::format(fmt, std::forward(args)...); -#ifndef TEST_HAS_NO_LOCALIZATION - if constexpr (std::same_as) - if (out != expected) - std::cerr << "\nFormat string " << fmt.get() << "\nExpected output " << expected << "\nActual output " << out - << '\n'; -#endif // TEST_HAS_NO_LOCALIZATION - assert(out == expected); + REQUIRE(out == expected, "\nFormat string ", fmt, "\nExpected output ", expected, "\nActual output ", out, '\n'); }; auto test_exception = [](std::string_view, std::basic_string_view, Args&&...) { diff --git a/libcxx/test/std/utilities/format/format.tuple/format.functions.vformat.pass.cpp b/libcxx/test/std/utilities/format/format.tuple/format.functions.vformat.pass.cpp --- a/libcxx/test/std/utilities/format/format.tuple/format.functions.vformat.pass.cpp +++ b/libcxx/test/std/utilities/format/format.tuple/format.functions.vformat.pass.cpp @@ -27,22 +27,12 @@ #include "test_macros.h" #include "format.functions.tests.h" - -#ifndef TEST_HAS_NO_LOCALIZATION -# include -# include -#endif +#include "test_validation.h" auto test = []( std::basic_string_view expected, std::basic_string_view fmt, Args&&... args) { std::basic_string out = std::vformat(fmt, std::make_format_args>(args...)); -#ifndef TEST_HAS_NO_LOCALIZATION - if constexpr (std::same_as) - if (out != expected) - std::cerr << "\nFormat string " << fmt << "\nExpected output " << expected << "\nActual output " << out - << '\n'; -#endif // TEST_HAS_NO_LOCALIZATION - assert(out == expected); + REQUIRE(out == expected, "\nFormat string ", fmt, "\nExpected output ", expected, "\nActual output ", out, '\n'); }; auto test_exception = @@ -53,22 +43,18 @@ #ifndef TEST_HAS_NO_EXCEPTIONS try { TEST_IGNORE_NODISCARD std::vformat(fmt, std::make_format_args>(args...)); -# if !defined(TEST_HAS_NO_LOCALIZATION) - if constexpr (std::same_as) - std::cerr << "\nFormat string " << fmt << "\nDidn't throw an exception.\n"; -# endif // !defined(TEST_HAS_NO_LOCALIZATION - assert(false); + FAIL("\nFormat string ", fmt, "\nDidn't throw an exception.\n"); } catch ([[maybe_unused]] const std::format_error& e) { -# if defined(_LIBCPP_VERSION) -# if !defined(TEST_HAS_NO_LOCALIZATION) - if constexpr (std::same_as) { - if (e.what() != what) - std::cerr << "\nFormat string " << fmt << "\nExpected exception " << what << "\nActual exception " - << e.what() << '\n'; - } -# endif // !defined(TEST_HAS_NO_LOCALIZATION - assert(e.what() == what); -# endif // defined(_LIBCPP_VERSION) + LIBCPP_REQUIRE( + e.what() == what, + "\nFormat string ", + fmt, + "\nExpected exception ", + what, + "\nActual exception ", + e.what(), + '\n'); + return; } assert(false); diff --git a/libcxx/test/support/test_validation.h b/libcxx/test/support/test_validation.h new file mode 100644 --- /dev/null +++ b/libcxx/test/support/test_validation.h @@ -0,0 +1,103 @@ +//===----------------------------------------------------------------------===// +// +// 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_TEST_VALIDATION_H +#define TEST_SUPPORT_TEST_VALIDATION_H + +// Contains a set of validation macros. +// +// Note these test were added after C++20 was well supported by the compilers +// used. To make the implementation simple the macros require C++20 or newer. +// It's not expected that existing tests start to use these new macros. +// +// These macros are an alternative to using assert. The differences are: +// - The assert message isn't localized. +// - It's possible to log additional information. This is useful when the +// function asserting is a helper function. In these cases the assertion +// failure contains to little information to find the issue. For example, in +// the format functions, the really useful information is the actual output, +// the expected output, and the format string used. These macros allow +// logging additional arguments. + +#include "test_macros.h" + +#include +#include + +#ifndef TEST_HAS_NO_LOCALIZATION +# include +#endif // TEST_HAS_NO_LOCALIZATION + +#if TEST_STD_VER > 17 + +# ifndef TEST_HAS_NO_LOCALIZATION +template +concept test_char_streamable = requires(T&& value) { std::cerr << std::forward(value); }; +# endif // TEST_HAS_NO_LOCALIZATION + +// Logs the error and calls exit. +// +// It show a generic assert like message and if possible streams the args to +// std::cerr else streams a default message. Not being able to stream is not +// considered and error. For example, streaming to std::wcerr doesn't work +// properly in the CI. Therefore the formatting tests should only stream to +// std::cerr. +template +[[noreturn]] void test_log_error(const char* condition, const char* file, int line, [[maybe_unused]] Args&&... args) { + const char* msg = condition ? "Assertion failure: " : "Unconditional failure:"; + fprintf(stderr, "%s%s %s %d\n", msg, condition, file, line); +# ifndef TEST_HAS_NO_LOCALIZATION + if constexpr ((test_char_streamable && ...)) + ((std::cerr << std::forward(args)), ...); + else + std::cerr << "Message discarded since it can't be streamed to std::cerr.\n"; +# endif + exit(EXIT_FAILURE); +} + +inline constexpr auto test_fail = [](const char* file, int line, Args&&... args) { + test_log_error("", file, line, std::forward(args)...); +}; + +inline constexpr auto test_require = + [](bool condition, const char* condition_str, const char* file, int line, Args&&... args) { + if (condition) + return; + + test_log_error(condition_str, file, line, std::forward(args)...); + }; + +inline constexpr auto test_libcpp_require = + []( + [[maybe_unused]] bool condition, + [[maybe_unused]] const char* condition_str, + [[maybe_unused]] const char* file, + [[maybe_unused]] int line, + [[maybe_unused]] Args&&... args) { +# if defined(_LIBCPP_VERSION) + test_require(condition, condition_str, file, line, std::forward(args)...); +# endif // _LIBCPP_VERSION + }; + +// assert(false) replacement +# define FAIL(...) test_fail(__FILE__, __LINE__ __VA_OPT__(, ) __VA_ARGS__) + +// assert replacement. +# define REQUIRE(CONDITION, ...) test_require(CONDITION, #CONDITION, __FILE__, __LINE__ __VA_OPT__(, ) __VA_ARGS__) + +// LIBCPP_ASSERT replacement +// +// This requirement is only tested when the test suite is used for libc++. +// This allows checking libc++ specific requirements, for example the error +// messages of exceptions. +# define LIBCPP_REQUIRE(CONDITION, ...) \ + test_libcpp_require(CONDITION, #CONDITION, __FILE__, __LINE__ __VA_OPT__(, ) __VA_ARGS__) + +#endif // TEST_STD_VER > 17 + +#endif // TEST_SUPPORT_TEST_VALIDATION_H