-
Notifications
You must be signed in to change notification settings - Fork 14.3k
[libc++][test] Adds transcode option. #73395
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
mordante
merged 1 commit into
llvm:main
from
mordante:GH-implements_transcoding_for_tests
Dec 8, 2023
Merged
[libc++][test] Adds transcode option. #73395
mordante
merged 1 commit into
llvm:main
from
mordante:GH-implements_transcoding_for_tests
Dec 8, 2023
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@llvm/pr-subscribers-libcxx Author: Mark de Wever (mordante) ChangesThis should make it easier to get better output when wchar_t tests fail. Differential Revision: https://reviews.llvm.org/D150593 Full diff: https://github.com/llvm/llvm-project/pull/73395.diff 2 Files Affected:
diff --git a/libcxx/test/std/time/time.syn/formatter_tests.h b/libcxx/test/std/time/time.syn/formatter_tests.h
index 42c176b3b47e5bf..1b343b5c8711b68 100644
--- a/libcxx/test/std/time/time.syn/formatter_tests.h
+++ b/libcxx/test/std/time/time.syn/formatter_tests.h
@@ -8,6 +8,8 @@
#ifndef TEST_STD_TIME_TIME_SYN_FORMATTER_TESTS_H
#define TEST_STD_TIME_TIME_SYN_FORMATTER_TESTS_H
+#include "assert_macros.h"
+#include "concat_macros.h"
#include "make_string.h"
#include "string_literal.h"
#include "test_format_string.h"
@@ -34,11 +36,9 @@ using format_context = std::format_context;
template <class CharT, class... Args>
void check(std::basic_string_view<CharT> expected, test_format_string<CharT, Args...> fmt, Args&&... args) {
std::basic_string<CharT> out = std::format(fmt, std::forward<Args>(args)...);
- if constexpr (std::same_as<CharT, char>)
- if (out != expected)
- std::cerr << "\nFormat string " << fmt.get() << "\nExpected output " << expected << "\nActual output " << out
- << '\n';
- assert(out == expected);
+ TEST_REQUIRE(out == expected,
+ TEST_WRITE_CONCATENATED(
+ "\nFormat string ", fmt.get(), "\nExpected output ", expected, "\nActual output ", out, '\n'));
}
template <class CharT, class... Args>
@@ -47,38 +47,24 @@ void check(const std::locale& loc,
test_format_string<CharT, Args...> fmt,
Args&&... args) {
std::basic_string<CharT> out = std::format(loc, fmt, std::forward<Args>(args)...);
- if constexpr (std::same_as<CharT, char>)
- if (out != expected)
- std::cerr << "\nFormat string " << fmt.get() << "\nExpected output " << expected << "\nActual output " << out
- << '\n';
- assert(out == expected);
+ TEST_REQUIRE(out == expected,
+ TEST_WRITE_CONCATENATED(
+ "\nFormat string ", fmt.get(), "\nExpected output ", expected, "\nActual output ", out, '\n'));
}
template <class CharT, class... Args>
void check_exception([[maybe_unused]] std::string_view what,
[[maybe_unused]] std::basic_string_view<CharT> fmt,
[[maybe_unused]] const Args&... args) {
-#ifndef TEST_HAS_NO_EXCEPTIONS
- try {
- TEST_IGNORE_NODISCARD std::vformat(fmt, std::make_format_args<format_context<CharT>>(args...));
- if constexpr (std::same_as<CharT, char>)
- std::cerr << "\nFormat string " << fmt << "\nDidn't throw an exception.\n";
- assert(false);
- } catch (const std::format_error& e) {
-# if defined(_LIBCPP_VERSION)
- if constexpr (std::same_as<CharT, char>)
- if (e.what() != what)
- std::cerr << "\nFormat string " << fmt << "\nExpected exception " << what << "\nActual exception "
- << e.what() << '\n';
- assert(e.what() == what);
-# else
- (void)what;
- (void)e;
-# endif
- return;
- }
- assert(false);
-#endif
+ TEST_VALIDATE_EXCEPTION(
+ std::format_error,
+ [&]([[maybe_unused]] const std::format_error& e) {
+ TEST_LIBCPP_REQUIRE(
+ e.what() == what,
+ TEST_WRITE_CONCATENATED(
+ "\nFormat string ", fmt, "\nExpected exception ", what, "\nActual exception ", e.what(), '\n'));
+ },
+ TEST_IGNORE_NODISCARD std::vformat(fmt, std::make_format_args<format_context<CharT>>(args...)));
}
template <class CharT, class T>
diff --git a/libcxx/test/support/concat_macros.h b/libcxx/test/support/concat_macros.h
index ea89c62df9a6590..6e3420f6c3e8d28 100644
--- a/libcxx/test/support/concat_macros.h
+++ b/libcxx/test/support/concat_macros.h
@@ -16,15 +16,153 @@
#include "test_macros.h"
#ifndef TEST_HAS_NO_LOCALIZATION
+# include <concepts>
+# include <iterator>
# include <sstream>
#endif
#if TEST_STD_VER > 17
# ifndef TEST_HAS_NO_LOCALIZATION
+
+[[nodiscard]] constexpr bool test_is_high_surrogate(char32_t value) { return value >= 0xd800 && value <= 0xdbff; }
+
+[[nodiscard]] constexpr bool test_is_low_surrogate(char32_t value) { return value >= 0xdc00 && value <= 0xdfff; }
+
+[[nodiscard]] constexpr bool test_is_surrogate(char32_t value) { return value >= 0xd800 && value <= 0xdfff; }
+
+[[nodiscard]] constexpr bool test_is_code_point(char32_t value) { return value <= 0x10ffff; }
+
+[[nodiscard]] constexpr bool test_is_scalar_value(char32_t value) {
+ return test_is_code_point(value) && !test_is_surrogate(value);
+}
+
+inline constexpr char32_t test_replacement_character = U'\ufffd';
+
+template <class InIt, class OutIt>
+OutIt test_transcode() = delete;
+
+template <class InIt, class OutIt>
+ requires(std::output_iterator<OutIt, const char&> && std::same_as<std::iter_value_t<InIt>, char8_t>)
+OutIt test_transcode(InIt first, InIt last, OutIt out_it) {
+ return std::copy(first, last, out_it);
+}
+
+template <class OutIt>
+ requires std::output_iterator<OutIt, const char&>
+void test_encode(OutIt& out_it, char16_t value) {
+ if (value < 0x80)
+ *out_it++ = value;
+ else if (value < 0x800) {
+ *out_it++ = 0b1100'0000 | (value >> 6);
+ *out_it++ = 0b1000'0000 | (value & 0b0011'1111);
+ } else {
+ *out_it++ = 0b1110'0000 | (value >> 12);
+ *out_it++ = 0b1000'0000 | ((value) >> 6 & 0b0011'1111);
+ *out_it++ = 0b1000'0000 | (value & 0b0011'1111);
+ }
+}
+
+template <class OutIt>
+ requires std::output_iterator<OutIt, const char&>
+void test_encode(OutIt& out_it, char32_t value) {
+ if ((value & 0xffff0000) == 0)
+ test_encode(out_it, static_cast<char16_t>(value));
+ else {
+ *out_it++ = 0b1110'0000 | (value >> 18);
+ *out_it++ = 0b1000'0000 | ((value) >> 12 & 0b0011'1111);
+ *out_it++ = 0b1000'0000 | ((value) >> 6 & 0b0011'1111);
+ *out_it++ = 0b1000'0000 | (value & 0b0011'1111);
+ }
+}
+
+template <class InIt, class OutIt>
+ requires(std::output_iterator<OutIt, const char&> &&
+ (std::same_as<std::iter_value_t<InIt>, char16_t>
+# ifndef TEST_HAS_NO_WIDE_CHARACTERS
+ || (std::same_as<std::iter_value_t<InIt>, wchar_t> && sizeof(wchar_t) == 2))
+# endif
+ )
+OutIt test_transcode(InIt first, InIt last, OutIt out_it) {
+ while (first != last) {
+ char32_t value = *first++;
+
+ if (test_is_low_surrogate(value)) [[unlikely]] {
+ test_encode(out_it, static_cast<char16_t>(test_replacement_character));
+ continue;
+ }
+
+ if (!test_is_high_surrogate(value)) {
+ test_encode(out_it, static_cast<char16_t>(value));
+ continue;
+ }
+
+ if (first == last || !test_is_low_surrogate(static_cast<char32_t>(*first))) [[unlikely]] {
+ test_encode(out_it, static_cast<char16_t>(test_replacement_character));
+ continue;
+ }
+
+ value -= 0xd800;
+ value <<= 10;
+ value += static_cast<char32_t>(*first++) - 0xdc00;
+ value += 0x10000;
+
+ if (test_is_code_point(value)) [[likely]]
+ test_encode(out_it, value);
+ else
+ test_encode(out_it, static_cast<char16_t>(test_replacement_character));
+ }
+
+ return out_it;
+}
+
+template <class InIt, class OutIt>
+ requires(std::output_iterator<OutIt, const char&> &&
+ (std::same_as<std::iter_value_t<InIt>, char32_t> ||
+# ifndef TEST_HAS_NO_WIDE_CHARACTERS
+ (std::same_as<std::iter_value_t<InIt>, wchar_t> && sizeof(wchar_t) == 4))
+# endif
+ )
+OutIt test_transcode(InIt first, InIt last, OutIt out_it) {
+ while (first != last) {
+ char32_t value = *first++;
+ if (test_is_code_point(value)) [[likely]]
+ test_encode(out_it, value);
+ else
+ test_encode(out_it, static_cast<char16_t>(test_replacement_character));
+ }
+ return out_it;
+}
+
template <class T>
-concept test_char_streamable = requires(T&& value) { std::stringstream{} << std::forward<T>(value); };
-# endif
+concept test_streamable = requires(std::stringstream& stream, T&& value) { stream << value; };
+
+template <class T>
+concept test_convertable = (!test_streamable<T> && requires(T&& value) {
+ std::basic_string_view{std::begin(value), std::end(value)};
+});
+
+template <class T>
+concept test_can_concat = test_streamable<T> || test_convertable<T>;
+
+template <test_streamable T>
+std::ostream& test_concat(std::ostream& stream, T&& value) {
+ return stream << value;
+}
+
+template <test_convertable T>
+std::ostream& test_concat(std::ostream& stream, T&& value) {
+ auto b = std::begin(value);
+ auto e = std::end(value);
+ if (b != e) {
+ // When T is an array it's string-literal, remove the NUL terminator.
+ if constexpr (std::is_array_v<std::remove_cvref_t<T>>)
+ --e;
+ test_transcode(b, e, std::ostream_iterator<char>{stream});
+ }
+ return stream;
+}
+# endif // TEST_HAS_NO_LOCALIZATION
// If possible concatenates message for the assertion function, else returns a
// default message. Not being able to stream is not considered and error. For
@@ -37,12 +175,12 @@ concept test_char_streamable = requires(T&& value) { std::stringstream{} << std:
template <class... Args>
std::string test_concat_message([[maybe_unused]] Args&&... args) {
# ifndef TEST_HAS_NO_LOCALIZATION
- if constexpr ((test_char_streamable<Args> && ...)) {
+ if constexpr ((test_can_concat<Args> && ...)) {
std::stringstream sstr;
- ((sstr << std::forward<Args>(args)), ...);
+ ((test_concat(sstr, std::forward<Args>(args))), ...);
return sstr.str();
} else
-# endif
+# endif // TEST_HAS_NO_LOCALIZATION
return "Message discarded since it can't be streamed to std::cerr.\n";
}
|
a9a0a99
to
bfc932d
Compare
ldionne
approved these changes
Dec 5, 2023
This should make it easier to get better output when wchar_t tests fail. The code is based on the Unicode transcoding in <format>. Differential Revision: https://reviews.llvm.org/D150593
bfc932d
to
b0eab04
Compare
This was referenced Dec 10, 2023
mordante
pushed a commit
that referenced
this pull request
Dec 10, 2023
…yntax damage (#74987) @mordante This was introduced by #73395 a couple of days ago. This is causing PR checks to fail, [stage3 (generic-no-wide-characters, libcxx-runners-8-set, OFF)](https://github.com/llvm/llvm-project/actions/runs/7154839054/job/19484723909?pr=74254#logs): ``` In file included from /home/runner/_work/llvm-project/llvm-project/libcxx/test/std/utilities/format/format.tuple/format.functions.vformat.pass.cpp:29: /home/runner/_work/llvm-project/llvm-project/libcxx/test/support/concat_macros.h:86:1: error: expected ')' 86 | OutIt test_transcode(InIt first, InIt last, OutIt out_it) { | ^ /home/runner/_work/llvm-project/llvm-project/libcxx/test/support/concat_macros.h:80:11: note: to match this '(' 80 | requires(std::output_iterator<OutIt, const char&> && | ^ /home/runner/_work/llvm-project/llvm-project/libcxx/test/std/utilities/format/format.tuple/format.functions.vformat.pass.cpp:63:2: error: expected unqualified-id 63 | } | ^ ```
StephanTLavavej
added a commit
that referenced
this pull request
Dec 10, 2023
Found while running libc++'s tests with MSVC's STL. * `libcxx/test/std/algorithms/alg.modifying.operations/alg.unique/ranges_unique_copy.pass.cpp` + Fix MSVC "warning C4389: '`==`': signed/unsigned mismatch". + This was x86-specific for me. The LHS is `int` and the RHS is `size_t`. We know the `array`'s size, so `static_cast<int>` is certainly safe, and this matches the following `numberOfProj` comparisons. * `libcxx/test/std/containers/sequences/insert_range_sequence_containers.h` + Fix MSVC "warning C4267: 'argument': conversion from '`size_t`' to '`const int`', possible loss of data". + `test_case.index` is `size_t`: https://github.com/llvm/llvm-project/blob/b85f1f9b182234ba366d78ae2174a149e44d08c1/libcxx/test/std/containers/insert_range_helpers.h#L65-L68 + But the container's `difference_type` is `int`: https://github.com/llvm/llvm-project/blob/b85f1f9b182234ba366d78ae2174a149e44d08c1/libcxx/test/support/test_allocator.h#L65-L76 + I introduced an alias `D` to make the long line more readable. * `libcxx/test/std/containers/unord/unord.map/eq.different_hash.pass.cpp` * `libcxx/test/std/containers/unord/unord.multimap/eq.different_hash.pass.cpp` * `libcxx/test/std/containers/unord/unord.multiset/eq.different_hash.pass.cpp` * `libcxx/test/std/containers/unord/unord.set/eq.different_hash.pass.cpp` + Fix MSVC "warning C6297: Arithmetic overflow. Results might not be an expected value." + This warning is almost annoying enough to outright disable, but we use similar `static_cast`s to deal with sign/truncation warnings elsewhere, because there's some value in ensuring that product code is clean with respect to these warnings. If there were many more occurrences, then disabling the warning would be appropriate. + Cleanup: Change 2 inconsistently unqualified occurrences of `size_t` to `std::size_t`. * `libcxx/test/std/containers/views/mdspan/layout_stride/index_operator.pass.cpp` + Fix MSVC "warning C4244: 'initializing': conversion from '`__int64`' to '`size_t`', possible loss of data". + This was x86-specific for me. The `args` are indeed `int64_t`, and we're storing the result in `size_t`, so we should cast. * `libcxx/test/std/ranges/range.utility/range.utility.conv/container.h` + Fix MSVC "warning C4244: 'initializing': conversion from '`ptrdiff_t`' to '`int`', possible loss of data". + Fix MSVC "warning C4267: 'initializing': conversion from '`size_t`' to '`int`', possible loss of data". + We're initializing `int size_`, so we should explicitly cast from pointer subtraction and `std::ranges::size`. * `libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.create/allocate_shared_for_overwrite.pass.cpp` * `libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.create/make_shared_for_overwrite.pass.cpp` * `libcxx/test/std/utilities/smartptr/unique.ptr/unique.ptr.create/make_unique_for_overwrite.default_init.pass.cpp` + Fix MSVC "warning C4309: 'initializing': truncation of constant value". + MSVC emits this warning because `0xDE` is outside the range of `char` (signed by default in our implementation). * `libcxx/test/support/concat_macros.h` + Fix MSVC "warning C4244: 'argument': conversion from '`char16_t`' to '`const char`', possible loss of data". + Fix MSVC "warning C4244: 'argument': conversion from '`unsigned int`' to '`const char`', possible loss of data". + This code was very recently introduced by @mordante in #73395.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
This should make it easier to get better output when wchar_t tests fail.
The code is based on the Unicode transcoding in
<format>
.Differential Revision: https://reviews.llvm.org/D150593