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 "assert_macros.h" #ifndef TEST_HAS_NO_LOCALIZATION # include @@ -35,19 +36,9 @@ 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); + TEST_REQUIRE(out == expected, + test_concat_message( + "\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 "assert_macros.h" #ifndef TEST_HAS_NO_LOCALIZATION # include @@ -41,19 +42,9 @@ 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); + TEST_REQUIRE(out == expected, + test_concat_message( + "\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,16 @@ #include "format_tests.h" #include "string_literal.h" #include "test_format_string.h" +#include "assert_macros.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); + TEST_REQUIRE( + out == expected, + test_concat_message( + "\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,16 @@ #include "format_tests.h" #include "string_literal.h" #include "test_format_string.h" - -#ifndef TEST_HAS_NO_LOCALIZATION -# include -# include -#endif +#include "assert_macros.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); + TEST_REQUIRE( + out == expected, + test_concat_message( + "\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 "assert_macros.h" #define STR(S) MAKE_STRING(CharT, S) #define SV(S) MAKE_STRING_VIEW(CharT, S) @@ -127,11 +128,9 @@ // *** 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); + TEST_REQUIRE(out == expected, + test_concat_message( + "\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,14 @@ #include "test_macros.h" #include "format_tests.h" #include "string_literal.h" +#include "assert_macros.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); + TEST_REQUIRE( + out == expected, + test_concat_message("\nFormat string ", fmt, "\nExpected output ", expected, "\nActual output ", out, '\n')); }; auto test_exception = @@ -37,10 +40,13 @@ [[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...)); + TEST_FAIL(test_concat_message("\nFormat string ", fmt, "\nDidn't throw an exception.\n")); } catch ([[maybe_unused]] const std::format_error& e) { - LIBCPP_ASSERT(e.what() == what); + TEST_LIBCPP_REQUIRE( + e.what() == what, + test_concat_message( + "\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,14 @@ #include "test_macros.h" #include "format_tests.h" #include "string_literal.h" +#include "assert_macros.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); + TEST_REQUIRE( + out == expected, + test_concat_message("\nFormat string ", fmt, "\nExpected output ", expected, "\nActual output ", out, '\n')); }; auto test_exception = @@ -37,9 +40,13 @@ #ifndef TEST_HAS_NO_EXCEPTIONS try { TEST_IGNORE_NODISCARD std::vformat(fmt, std::make_format_args>(args...)); - assert(false); + TEST_FAIL(test_concat_message("\nFormat string ", fmt, "\nDidn't throw an exception.\n")); } catch ([[maybe_unused]] const std::format_error& e) { - LIBCPP_ASSERT(e.what() == what); + TEST_LIBCPP_REQUIRE( + e.what() == what, + test_concat_message( + "\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,14 @@ #include "format.functions.tests.h" #include "test_format_string.h" #include "test_macros.h" - -#ifndef TEST_HAS_NO_LOCALIZATION -# include -# include -#endif +#include "assert_macros.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); + TEST_REQUIRE( + out == expected, + test_concat_message("\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 @@ -29,22 +29,14 @@ #include "test_macros.h" #include "format.functions.tests.h" - -#ifndef TEST_HAS_NO_LOCALIZATION -# include -# include -#endif +#include "assert_macros.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); + TEST_REQUIRE( + out == expected, + test_concat_message("\nFormat string ", fmt, "\nExpected output ", expected, "\nActual output ", out, '\n')); }; auto test_exception = @@ -55,22 +47,13 @@ #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); + TEST_FAIL(test_concat_message("\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) + TEST_LIBCPP_REQUIRE( + e.what() == what, + test_concat_message( + "\nFormat string ", fmt, "\nExpected exception ", what, "\nActual exception ", e.what(), '\n')); + return; } assert(false); diff --git a/libcxx/test/support/assert_macros.h b/libcxx/test/support/assert_macros.h new file mode 100644 --- /dev/null +++ b/libcxx/test/support/assert_macros.h @@ -0,0 +1,106 @@ +//===----------------------------------------------------------------------===// +// +// 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_ASSERT_MACROS_H +#define TEST_SUPPORT_ASSERT_MACROS_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 + +#if TEST_STD_VER > 17 + +# ifndef TEST_HAS_NO_LOCALIZATION +template +concept test_char_streamable = requires(T&& value) { std::stringstream{} << std::forward(value); }; +# endif + +// If possible concatenates message for the assertion function, else returns 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::string_string. +template +std::string test_concat_message([[maybe_unused]] Args&&... args) { +# ifndef TEST_HAS_NO_LOCALIZATION + if constexpr ((test_char_streamable && ...)) { + std::stringstream sstr; + ((sstr << std::forward(args)), ...); + return sstr.str(); + } else +# endif + return "Message discarded since it can't be streamed to std::cerr.\n"; +} + +#endif // TEST_STD_VER > 17 + +// Logs the error and calls exit. +// +// It shows a generic assert like message including a custom message. This +// message should end with a newline. +[[noreturn]] void test_log_error(const char* condition, const char* file, int line, std::string&& message) { + const char* msg = condition ? "Assertion failure: " : "Unconditional failure:"; + std::fprintf(stderr, "%s%s %s %d\n%s", msg, condition, file, line, message.c_str()); + exit(EXIT_FAILURE); +} + +inline void test_fail(const char* file, int line, std::string&& message) { + test_log_error("", file, line, std::move(message)); +} + +inline void test_require(bool condition, const char* condition_str, const char* file, int line, std::string&& message) { + if (condition) + return; + + test_log_error(condition_str, file, line, std::move(message)); +} + +inline void test_libcpp_require( + [[maybe_unused]] bool condition, + [[maybe_unused]] const char* condition_str, + [[maybe_unused]] const char* file, + [[maybe_unused]] int line, + [[maybe_unused]] std::string&& message) { +#if defined(_LIBCPP_VERSION) + test_require(condition, condition_str, file, line, std::move(message)); +#endif +} + +// assert(false) replacement +#define TEST_FAIL(MSG) ::test_fail(__FILE__, __LINE__, MSG) + +// assert replacement. +#define TEST_REQUIRE(CONDITION, MSG) ::test_require(CONDITION, #CONDITION, __FILE__, __LINE__, MSG) + +// 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 TEST_LIBCPP_REQUIRE(CONDITION, MSG) ::test_libcpp_require(CONDITION, #CONDITION, __FILE__, __LINE__, MSG) + +#endif // TEST_SUPPORT_ASSERT_MACROS_H