13f65f718SMark de Wever //===----------------------------------------------------------------------===// 2*6a54dfbfSLouis Dionne // 33f65f718SMark de Wever // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 43f65f718SMark de Wever // See https://llvm.org/LICENSE.txt for license information. 53f65f718SMark de Wever // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 63f65f718SMark de Wever // 73f65f718SMark de Wever //===----------------------------------------------------------------------===// 83f65f718SMark de Wever 93f65f718SMark de Wever // UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 103f65f718SMark de Wever // UNSUPPORTED: no-filesystem 113f65f718SMark de Wever // UNSUPPORTED: no-wide-characters 1204a75f54SLouis Dionne // UNSUPPORTED: libcpp-has-no-unicode 133f65f718SMark de Wever // UNSUPPORTED: GCC-ALWAYS_INLINE-FIXME 143f65f718SMark de Wever 153f65f718SMark de Wever // Clang modules do not work with the definiton of _LIBCPP_TESTING_PRINT_WRITE_TO_WINDOWS_CONSOLE_FUNCTION 163401b308SMark de Wever // XFAIL: clang-modules-build 173f65f718SMark de Wever 183f65f718SMark de Wever // XFAIL: availability-fp_to_chars-missing 193f65f718SMark de Wever 203f65f718SMark de Wever // <print> 213f65f718SMark de Wever 223f65f718SMark de Wever // Tests the implementation of 233f65f718SMark de Wever // void __print::__vprint_unicode_windows(FILE* __stream, string_view __fmt, 243f65f718SMark de Wever // format_args __args, bool __write_nl, 253f65f718SMark de Wever // bool __is_terminal); 263f65f718SMark de Wever // 273f65f718SMark de Wever // In the library when the stdout is redirected to a file it is no 283f65f718SMark de Wever // longer considered a terminal and the special terminal handling is no 293f65f718SMark de Wever // longer executed. By testing this function we can "force" emulate a 303f65f718SMark de Wever // terminal. 313f65f718SMark de Wever // Note __write_nl is tested by the public API. 323f65f718SMark de Wever 333f65f718SMark de Wever #include <string_view> 343f65f718SMark de Wever #include <cstdio> 353f65f718SMark de Wever #include <algorithm> 363f65f718SMark de Wever #include <cassert> 373f65f718SMark de Wever 383f65f718SMark de Wever void write_to_console(FILE*, std::wstring_view data); 393f65f718SMark de Wever #define _LIBCPP_TESTING_PRINT_WRITE_TO_WINDOWS_CONSOLE_FUNCTION ::write_to_console 403f65f718SMark de Wever #include <print> 413f65f718SMark de Wever 423f65f718SMark de Wever #include "test_macros.h" 433f65f718SMark de Wever #include "filesystem_test_helper.h" 443f65f718SMark de Wever #include "make_string.h" 453f65f718SMark de Wever 463f65f718SMark de Wever TEST_GCC_DIAGNOSTIC_IGNORED("-Wuse-after-free") 473f65f718SMark de Wever 483f65f718SMark de Wever #define SV(S) MAKE_STRING_VIEW(wchar_t, S) 493f65f718SMark de Wever 503f65f718SMark de Wever bool calling = false; 513f65f718SMark de Wever std::wstring_view expected = L" world"; 523f65f718SMark de Wever 533f65f718SMark de Wever void write_to_console(FILE*, std::wstring_view data) { 543f65f718SMark de Wever assert(calling); 553f65f718SMark de Wever assert(data == expected); 563f65f718SMark de Wever } 573f65f718SMark de Wever 583f65f718SMark de Wever scoped_test_env env; 593f65f718SMark de Wever std::string filename = env.create_file("output.txt"); 603f65f718SMark de Wever 613f65f718SMark de Wever static void test_basics() { 623f65f718SMark de Wever FILE* file = std::fopen(filename.c_str(), "wb"); 633f65f718SMark de Wever assert(file); 643f65f718SMark de Wever 653f65f718SMark de Wever // Test writing to a "non-terminal" stream does not call WriteConsoleW. 663f65f718SMark de Wever std::__print::__vprint_unicode_windows(file, "Hello", std::make_format_args(), false, false); 673f65f718SMark de Wever assert(std::ftell(file) == 5); 683f65f718SMark de Wever 693f65f718SMark de Wever // It's not possible to reliably test whether writing to a "terminal" stream 703f65f718SMark de Wever // flushes before writing. Testing flushing a closed stream worked on some 713f65f718SMark de Wever // platforms, but was unreliable. 723f65f718SMark de Wever calling = true; 733f65f718SMark de Wever std::__print::__vprint_unicode_windows(file, " world", std::make_format_args(), false, true); 743f65f718SMark de Wever } 753f65f718SMark de Wever 763f65f718SMark de Wever // When the output is a file the data is written as-is. 773f65f718SMark de Wever // When the output is a "terminal" invalid UTF-8 input is flagged. 783f65f718SMark de Wever static void test(std::wstring_view output, std::string_view input) { 793f65f718SMark de Wever // *** File *** 803f65f718SMark de Wever FILE* file = std::fopen(filename.c_str(), "wb"); 813f65f718SMark de Wever assert(file); 823f65f718SMark de Wever 833f65f718SMark de Wever std::__print::__vprint_unicode_windows(file, input, std::make_format_args(), false, false); 843f65f718SMark de Wever assert(std::ftell(file) == static_cast<long>(input.size())); 853f65f718SMark de Wever std::fclose(file); 863f65f718SMark de Wever 873f65f718SMark de Wever file = std::fopen(filename.c_str(), "rb"); 883f65f718SMark de Wever assert(file); 893f65f718SMark de Wever 903f65f718SMark de Wever std::vector<char> buffer(input.size()); 913f65f718SMark de Wever size_t read = fread(buffer.data(), 1, buffer.size(), file); 923f65f718SMark de Wever assert(read == input.size()); 933f65f718SMark de Wever assert(input == std::string_view(buffer.begin(), buffer.end())); 943f65f718SMark de Wever std::fclose(file); 953f65f718SMark de Wever 963f65f718SMark de Wever // *** Terminal *** 973f65f718SMark de Wever expected = output; 983f65f718SMark de Wever std::__print::__vprint_unicode_windows(file, input, std::make_format_args(), false, true); 993f65f718SMark de Wever } 1003f65f718SMark de Wever 1013f65f718SMark de Wever static void test() { 1023f65f718SMark de Wever // *** Test valid UTF-8 *** 1033f65f718SMark de Wever #define TEST(S) test(SV(S), S) 1043f65f718SMark de Wever TEST("hello world"); 1053f65f718SMark de Wever 1063f65f718SMark de Wever // copied from benchmarks/std_format_spec_string_unicode.bench.cpp 1073f65f718SMark de Wever TEST("Lorem ipsum dolor sit amet, ne sensibus evertitur aliquando his. Iuvaret fabulas qui ex."); 1083f65f718SMark de Wever TEST("Lōrem ipsūm dolor sīt æmeÞ, ea vel nostrud feuġǣit, muciūs tēmporiȝusrefērrēnÞur no mel."); 1093f65f718SMark de Wever TEST("Лорем ипсум долор сит амет, еу диам тамяуам принципес вис, еяуидем цонцептам диспутандо"); 1103f65f718SMark de Wever TEST("入ト年媛ろ舗学ラロ準募ケカ社金スノ屋検れう策他セヲシ引口ぎ集7独ぱクふ出車ぽでぱ円輪ルノ受打わ。"); 1113f65f718SMark de Wever TEST("\U0001f636\u200d\U0001f32b\ufe0f"); 1123f65f718SMark de Wever #undef TEST 1133f65f718SMark de Wever 1143f65f718SMark de Wever // *** Test invalid utf-8 *** 1153f65f718SMark de Wever test(SV("\ufffd"), "\xc3"); 1163f65f718SMark de Wever test(SV("\ufffd("), "\xc3\x28"); 1173f65f718SMark de Wever 1183f65f718SMark de Wever // surrogate range 1193f65f718SMark de Wever test(SV("\ufffd"), "\xed\xa0\x80"); // U+D800 1203f65f718SMark de Wever test(SV("\ufffd"), "\xed\xaf\xbf"); // U+DBFF 1213f65f718SMark de Wever test(SV("\ufffd"), "\xed\xbf\x80"); // U+DC00 1223f65f718SMark de Wever test(SV("\ufffd"), "\xed\xbf\xbf"); // U+DFFF 1233f65f718SMark de Wever 1243f65f718SMark de Wever // beyond valid values 1253f65f718SMark de Wever test(SV("\ufffd"), "\xf4\x90\x80\x80"); // U+110000 1263f65f718SMark de Wever test(SV("\ufffd"), "\xf4\xbf\xbf\xbf"); // U+11FFFF 1273f65f718SMark de Wever 1283f65f718SMark de Wever // Validates http://unicode.org/review/pr-121.html option 3. 1293f65f718SMark de Wever test(SV("\u0061\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\u0062"), "\x61\xf1\x80\x80\xe1\x80\xc2\x62"); 1303f65f718SMark de Wever } 1313f65f718SMark de Wever 1323f65f718SMark de Wever int main(int, char**) { 1333f65f718SMark de Wever test_basics(); 1343f65f718SMark de Wever test(); 1353f65f718SMark de Wever } 136